Audio Unit和ExtendedAudioFile播放音頻

前言

相關(guān)文章:
使用VideoToolbox硬編碼H.264
使用VideoToolbox硬解碼H.264
使用AudioToolbox編碼AAC
使用AudioToolbox播放AAC
HLS點(diǎn)播實(shí)現(xiàn)(H.264和AAC碼流)
HLS推流的實(shí)現(xiàn)(iOS和OS X系統(tǒng))
iOS在線音頻流播放
Audio Unit播放PCM文件
Audio Unit錄音(播放伴奏+耳返)
Audio Unit播放aac/m4a/mp3等文件
前文介紹了AudioUnit的錄音/播放功能,也介紹了通過(guò)AudioConvert進(jìn)行音頻的轉(zhuǎn)換,但是AudioConvert的API使用起來(lái)較為麻煩,除了需要調(diào)用AudioFileGetProperty獲取許多信息之外,還要調(diào)用AudioConverterFillComplexBuffer進(jìn)行ConvertBuffer的填充,并在其數(shù)據(jù)輸入回調(diào)中調(diào)用AudioFileReadPacketData,且要考慮AudioStreamPacketDescription的賦值。
本文嘗試使用更為簡(jiǎn)單的方法 Extended Audio File Services
Extended Audio File Services是high-level的API,提供音頻文件的讀/寫(xiě),是Audio File Services 和 Audio Converter Services 的結(jié)合,在AudioFile和AudioConvert的基礎(chǔ)上提供統(tǒng)一的接口進(jìn)行讀寫(xiě)操作。

正文

概念儲(chǔ)備

  • ExtAudioFileOpenURL是新建一個(gè)ExtAudioFileRef,用于讀取音頻文件;
  • ExtAudioFileWrapAudioFileID是通過(guò)一個(gè)已有的AudioFileID,創(chuàng)建一個(gè)ExtAudioFileRef;
    開(kāi)發(fā)者必須保證在ExtAudioFileRef被銷(xiāo)毀前,AudioFileID是處于打開(kāi)的狀態(tài),并且在ExtAudioFileRef被銷(xiāo)毀后,手動(dòng)關(guān)閉AudioFileID;
  • ExtAudioFileGetProperty 獲取對(duì)應(yīng)PropertyID的屬性;
  • ExtAudioFileGetProperty 獲取設(shè)置PropertyID的屬性;
    ExtAudioFile對(duì)應(yīng)的PropertyID如下
CF_ENUM(ExtAudioFilePropertyID) {
    kExtAudioFileProperty_FileDataFormat        = 'ffmt',   // AudioStreamBasicDescription
    kExtAudioFileProperty_FileChannelLayout     = 'fclo',   // AudioChannelLayout
    kExtAudioFileProperty_ClientDataFormat      = 'cfmt',   // AudioStreamBasicDescription
    kExtAudioFileProperty_ClientChannelLayout   = 'cclo',   // AudioChannelLayout
    kExtAudioFileProperty_CodecManufacturer     = 'cman',   // UInt32
    
    // read-only:
    kExtAudioFileProperty_AudioConverter        = 'acnv',   // AudioConverterRef
    kExtAudioFileProperty_AudioFile             = 'afil',   // AudioFileID
    kExtAudioFileProperty_FileMaxPacketSize     = 'fmps',   // UInt32
    kExtAudioFileProperty_ClientMaxPacketSize   = 'cmps',   // UInt32
    kExtAudioFileProperty_FileLengthFrames      = '#frm',   // SInt64
    
    // writable:
    kExtAudioFileProperty_ConverterConfig       = 'accf',   // CFPropertyListRef
    kExtAudioFileProperty_IOBufferSizeBytes     = 'iobs',   // UInt32
    kExtAudioFileProperty_IOBuffer              = 'iobf',   // void *
    kExtAudioFileProperty_PacketTable           = 'xpti'    // AudioFilePacketTableInfo
};

介紹其中常用的屬性:

  • kExtAudioFileProperty_FileDataFormat:讀取文件格式,只讀,返回文件的ASBD;

  • kAudioFormatProperty_FormatInfo:根據(jù)給定的格式,盡可能填充格式的其他信息。

  • kExtAudioFileProperty_ClientDataFormat:設(shè)置這個(gè)屬性,才能進(jìn)行對(duì)非pcm格式的文件進(jìn)行編解碼,這個(gè)格式也是ExtAudioFileRead 和 ExtAudioFileWrite 時(shí)的格式。

  • kExtAudioFileProperty_FileLengthFrames:文件的長(zhǎng)度,單位是sample frames,獲取前需要先設(shè)置好輸入和輸出的格式;

  • kExtAudioFileProperty_AudioConverter,是獲取系統(tǒng)的AudioConverterRef,如果在獲取之后,手動(dòng)修改converter的屬性,比如說(shuō)碼率,必須通過(guò)kExtAudioFileProperty_ConverterConfig設(shè)置ExtAudioFileRef;

  • kExtAudioFileError_CodecUnavailableInputConsumed:當(dāng)ExtAudioFileWrite被打斷的時(shí)候會(huì)返回這個(gè)錯(cuò)誤,需要先停止調(diào)用ExtAudioFileWrite,等待audioSession恢復(fù),并調(diào)用AudioSessionSetActive,再進(jìn)行resuming;與kExtAudioFileError_CodecUnavailableInputNotConsumed的區(qū)別是,前者的buffer已經(jīng)被使用,下次調(diào)用需要賦值新的buffer,后者需要再次提供相同的buffer;

具體細(xì)節(jié)

  • 1、初始化AVAudioSession和AudioBufferList;

  • 2、通過(guò)url打開(kāi)ExtAudioFileRef,并通過(guò)ExtAudioFileGetProperty獲取文件格式;初始化讀取的格式,并通過(guò)ExtAudioFileSetProperty設(shè)置給ExtAudioFileRef;輸入和輸出格式設(shè)置,類(lèi)似初始化AudioConvert的過(guò)程。

  • 3、初始化AudioUnit,并設(shè)置輸入的格式與ExtAudioFileRef的輸出格式一致;

  • 4、在AudioUnit的播放回調(diào)中調(diào)用ExtAudioFileRead讀取ExtAudioFileRef的數(shù)據(jù),如果讀取返回的數(shù)組長(zhǎng)度是0表示播放結(jié)束;

demo播放

遇到的問(wèn)題

1、獲取的音頻frame幀數(shù)不正常

如果在未設(shè)置好輸入輸出格式前,就通過(guò)kExtAudioFileProperty_FileLengthFrames獲取的總frame數(shù),此時(shí)獲取的frame是不準(zhǔn)確的,并且會(huì)導(dǎo)致后續(xù)的操作錯(cuò)誤。
正確的做法是先設(shè)置好 kExtAudioFileProperty_ClientDataFormat屬性的值,再獲取總的frame數(shù)。

2、播放進(jìn)度不準(zhǔn)確

播放的進(jìn)度=當(dāng)前播放的幀數(shù)/音頻文件的總幀數(shù);
進(jìn)度不準(zhǔn)確問(wèn)題是因?yàn)楂@取的是frame數(shù),之前在計(jì)算已播放的幀數(shù)時(shí)沒(méi)有正確的把讀取的字節(jié)長(zhǎng)度除以輸出格式的mBytesPerFrame。
當(dāng)前已播放的幀數(shù) += 讀取的字節(jié)長(zhǎng)度 / ASBD.mBytesPerFrame。

總結(jié)

ExtendedAudioFile相對(duì)Audio File Services 和 Audio Converter Services ,API調(diào)用非常簡(jiǎn)單和明確,并且不需要去處理AudioStreamPacketDescription,在實(shí)際開(kāi)發(fā)中邏輯更為清晰。
demo 的代碼在這里,可以看到ExtendedAudioFile具體使用方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容