1. 前言
在iOS中你雌,AVFoundation是一個集視頻播放器联、播放緩存、視頻轉(zhuǎn)碼婿崭、圖層混合拨拓、混音、變調(diào)氓栈、變速等諸多功能的多媒體庫渣磷,在iOS短視頻SDK中,使用到了
AVFoundation的硬解和播放模塊授瘦,以下將介紹短視頻SDK中對這些模塊的應(yīng)用實踐和遇到的問題以及解決方案醋界。
2. 基本概念
- 解碼:將壓縮數(shù)據(jù)還原為未壓縮數(shù)據(jù)竟宋,關(guān)于利用VideoToolbox硬解H.264可以參考這篇文章;
- 編碼:將原始數(shù)據(jù)進行壓縮生成另一種格式形纺;
- 轉(zhuǎn)碼:將已壓縮的視頻碼流轉(zhuǎn)換成另一種視頻碼流丘侠;
3. 問題及解決方案
在AVFoundation中提供了多層可用于播放的組件,例如AVPlayerViewController和AVPlayer逐样,這些系統(tǒng)組件可以滿足視頻的基本播放功能蜗字,在項目中,我們采用了AVPlayer進行播放預(yù)覽脂新,但使用中遇到不少問題挪捕,后改用AVAssetReader做解碼,對解碼后的數(shù)據(jù)進行處理后做預(yù)覽争便。下面介紹播放預(yù)覽中系統(tǒng)組件的一些使用注意事項级零。
3.1 MediaToolBox使用注意事項
在使用AVPlayer做音頻變調(diào)、混響的預(yù)覽時滞乙,用到了MTAudioProcessingTap
妄讯,該類的所有回調(diào)是以函數(shù)指針存放于結(jié)構(gòu)體中:
typedef struct {
int version;
void* CM_NULLABLE clientInfo;
MTAudioProcessingTapInitCallback CM_NULLABLE init;
MTAudioProcessingTapFinalizeCallback CM_NULLABLE finalize;
MTAudioProcessingTapPrepareCallback CM_NULLABLE prepare;
MTAudioProcessingTapUnprepareCallback CM_NULLABLE unprepare;
MTAudioProcessingTapProcessCallback CM_NONNULL process;
} MTAudioProcessingTapCallbacks;
OC與C函數(shù)交互時,我們會在clientInfo
變量中存放OC對象酷宵,在C語言函數(shù)的回調(diào)方法里使用__bridge
的方式獲取OC對象。
當(dāng)OC對象釋放時躬窜,MTAudioProcessingTap
的callback
才返回浇垦,在回調(diào)的C函數(shù)里面獲取到的clientInfo
就是野指針,crash就產(chǎn)生了荣挨。
在clientInfo
指向的對象被釋放時男韧,需要保存已釋放的狀態(tài),在回調(diào)里首先檢查該狀態(tài)默垄,判斷當(dāng)前對象是否釋放此虑,以避免造成野指針訪問。
3.2 AVAssetReader使用注意事項
AVAssetReader可用于讀取AVAsset媒體資源的軌道數(shù)據(jù)口锭,支持解碼朦前、格式轉(zhuǎn)換、mix等操作鹃操。但注意事項也不少:
-
AVAssetReader不可重復(fù)調(diào)用
startReading
韭寸,當(dāng)出現(xiàn)fail或complete狀態(tài)后也不能重復(fù)調(diào)用; - AVAssetReader做解碼的時候荆隘,切換后臺/來電會失去GPU權(quán)限恩伺,造成解碼失敗,AVAssetReader也變成fail狀態(tài)椰拒。異常打斷結(jié)束后晶渠,需要重啟reader凰荚,并確定reader重啟成功,否則需要retry褒脯;
-
AVAssetReader啟動后調(diào)用
seek
時便瑟,并不會很精準(zhǔn)seek
到目標(biāo)點,一般會比指定的時間早幾幀(AVPlayer的精準(zhǔn)seek憨颠,也有同樣的問題)胳徽,需要記錄seek
的目標(biāo)時間點,如果seek
后讀取出的buffer攜帶的 pts比seek
的目標(biāo)時間小爽彤,需要拋棄該數(shù)據(jù)养盗; -
AVAssetReaderOutput不可重復(fù)添加,也不可在AssetReader調(diào)用
startReading
后添加适篙; -
AVAssetReaderOutput不可在未添加前調(diào)用
copyNextSampleBuffer
往核; -
AVAssetReader釋放資源時,需要調(diào)用
cancelReading
來釋放 AVAsset資源嚷节,否則會出現(xiàn)fetch
不到該資源的問題聂儒; - AVAssetReader對文件視頻首幀非關(guān)鍵幀的視頻會解碼失敗,這說明AVAssetReader對文件格式要求很嚴格硫痰,不夠魯棒衩婚;
- AVAssetReader不支持m3u8文件,回出現(xiàn)讀取不到軌道信息的情況效斑,如果需要解析HLS視頻非春,需要使用FFMpeg進行解封裝和VideoToolBox解碼;
- AVAssetReader內(nèi)部創(chuàng)建了解碼器和緩存列表缓屠,但解碼器數(shù)量是有限制的(同AVPlayerItem)奇昙。
當(dāng)然AVAssetReader 只做demux,不做解碼工作時可以避免上述一些問題敌完,但需要自行使用VideoToolBox進行硬解储耐,pixel format轉(zhuǎn)換也得單獨處理。
3.3 AudioQueue使用注意事項
AudioQueue可進行音頻播放滨溉,開播前會預(yù)緩存一定數(shù)量的buffer數(shù)據(jù)什湘。在allocate buffer
時,需要設(shè)置buffer的大小业踏,該大小需要根據(jù)audio data format來設(shè)置禽炬,正常播放沒有問題,但播放速度非1.0的情況下勤家,buffer太小時或太大腹尖,都會有異常的問題,需要考慮的有mBytesPerFrame
、mChannelsPerFrame
以及mSampleRate
热幔。
而解碼后得到的音頻frame buffer中采樣數(shù)并不固定乐设,當(dāng)多音頻播放時,需要考慮是否存在audio data format變化的問題绎巨。
當(dāng)然近尚,每次切換音頻,重啟AudioQueue也是一種方案场勤。
AudioQueue的數(shù)據(jù)獲取采用的是pull
模式戈锻。在
AudioQueueOutputCallback
的回調(diào)中,需要Enqueue
待緩存的AudioQueueBufferRef
和媳。
當(dāng)Enqueue
的時候格遭,可能會觸發(fā)AudioQueueStop
或者AudioQueueDispose
,盡管inImmediate
設(shè)置為true
留瞳,也會造成假死一段時間拒迅,需要在 AudioQueueOutputCallback
的回調(diào)函數(shù)中先檢查狀態(tài)是否需要停止,如果為正常狀態(tài)她倘,則Enqueue buffer
璧微,否則flush
掉當(dāng)前 AudioQueue的數(shù)據(jù)。
4. 結(jié)語
以上是iOS短視頻使用到的AVFoundation組件時遇到的問題硬梁,在 金山云多媒體SDK中硬編直接使用VideoToolBox做編碼前硫,避免了一些AVAssetWriter的問題,此處未做贅述荧止。
以上是遇到的一些問題开瞭,歡迎指正。