Audio Unit播放aac/m4a/mp3等檔案

b10l07發表於2017-10-09

前言

相關文章:
使用VideoToolbox硬編碼H.264
使用VideoToolbox硬解碼H.264
使用AudioToolbox編碼AAC
使用AudioToolbox播放AAC
HLS點播實現(H.264和AAC碼流)
HLS推流的實現(iOS和OS X系統)
iOS線上音訊流播放
Audio Unit播放PCM檔案
Audio Unit錄音(播放伴奏+耳返)
前面兩篇介紹了Audio Unit播放PCM檔案邊錄邊播,這次引入AudioConvert實現aac/m4a/mp3格式的播放。

正文

1、格式轉換

音訊資料的格式轉換包括取樣率改變,單聲道到多聲道的轉變、音調的升高降低等,audio unit有一個專門格式轉換unit(kAudioUnitType_FormatConverter,type of 'aufc')。
AudioUnit不支援vbr的資料,也不支援從一個有失真壓縮格式轉換為pcm或者pcm轉換為有損格式,對於有損格式的音訊資料轉換,需要用CoreAudio的Audio Converter API。

2、AudioFile API 和 Converter

AudioFile API提供了API對音訊檔案的建立、開啟、修改和儲存;
Audio Converters 用於音訊檔案的編解碼,還可以用於sample rate的改變、int到float的轉變,最常見是將音訊檔案轉成pcm播放;
下面Converter的兩個格式:

Source Format
Sample Rate:              44100
Format ID:                 .mp3
Format Flags:                 0
Bytes per Packet:             0
Frames per Packet:         1152
Bytes per Frame:              0
Channels per Frame:           2
Bits per Channel:             0

Target Format
Sample Rate:              44100
Format ID:                 lpcm
Format Flags:                 4
Bytes per Packet:             2
Frames per Packet:            1
Bytes per Frame:              2
Channels per Frame:           1
Bits per Channel:            16

3、具體細節

1、初始化AudioFile,通過AudioFileOpenURL開啟音訊檔案,並讀取對應的音訊格式(AudioStreamBasicDescription);這裡和Audio Unit播放PCM檔案不同的是,還需要讀取kAudioFilePropertyMaximumPacketSizekAudioFilePropertyAudioDataPacketCount兩個屬性,分別是單個package的最大size和packet的數量,並通過快取的大小和package的size建立AudioStreamPacketDescription的陣列;
2、初始化AudioUnit,設定AVAudioSession的Category為AVAudioSessionCategoryPlayback;初始化AudioBufferList,設定AudioUnit的playback回撥;
3、在AudioUnit的playback回撥中,呼叫AudioConvert的AudioConverterFillComplexBuffer函式並設定好回撥方法lyInInputDataProc;在回撥的lyInInputDataProc中,通過AudioFileReadPacketData讀取音訊資料並把讀取的AudioStreamPacketDescription回傳;
4、AudioConvert轉換後的音訊資料會填入引數buffList,將對應的資料複製給AudioUnit的playback引數;

遇到的問題

1、API替換

一開始用的是AudioFileReadPackets方法讀取音訊資料,後面在遇到問題後發現AudioFileReadPackets被替換成AudioFileReadPacketData,引數類似;

2、AudioConverter的轉換函式的返回值

呼叫AudioConverterFillComplexBuffer後,在回撥方法lyInInputDataProc中,如果設定 *ioNumberDataPackets = 0,並且返回 noErr, AudioConverter 會進入 Finished 的狀態;

返回非零的值,表示資料未完成,比如在demo中返回了NO_MORE_DATANO_MORE_DATA是自定義的非零返回值;

3、AudioConverterNewSpecific返回-50

通過OSStatus,可以看到-50是AVAudioSessionErrorCodeBadParam 引數不一致;
檢查程式碼,發現是在使用AudioConverterNewSpecific() 建立轉換器的時候輸入流格式與輸出流格式的聲道數設定不同;(解決方案就是聲道數改成一致)

4、AudioConverterFillComplexBuffer返回561015652

通過OSStatus,查到561015652是kAudioConverterErr_RequiresPacketDescriptionsError = '!pkd',意思是沒有回撥AudioStreamPacketDescriptions引數;
對於音訊格式mBytesPerPacket=0的資料,需要AudioStreamPacketDescriptions引數來輔助轉換音訊資料;
解決方案就是新建AudioStreamPacketDescriptions陣列,並且在讀取後賦值給outDataPacketDescription(見demo);

總結

AudioUnit和AudioConvert的API雖然簡單,卻是功能強大。
文章中的介紹更多是自己在學習過程中的一些收穫,對於知識點的介紹很多是不夠全面和仔細的,對此建議看看參考目錄
Extended Audio File ServicesAudio File ServicesAudio Converter Services 的結合,提供統一的介面進行處理,下篇可能會是Extended Audio File相關。

參考

Playing a sound file using the Default Output Audio Unit
Supported Audio File and Data Formats in OS X

相關文章