iOS短視頻SDK中的AVFoundation實踐

1. 前言

iOS中,AVFoundation是一個集視頻播放、播放緩存、視頻轉碼、圖層混合、混音、變調、變速等諸多功能的多媒體庫,在iOS短視頻SDK中,使用到了
AVFoundation的硬解和播放模塊,以下將介紹短視頻SDK中對這些模塊的應用實踐和遇到的問題以及解決方案。

2. 基本概念

  1. 解碼:將壓縮數據還原為未壓縮數據,關于利用VideoToolbox硬解H.264可以參考這篇文章
  2. 編碼:將原始數據進行壓縮生成另一種格式;
  3. 轉碼:將已壓縮的視頻碼流轉換成另一種視頻碼流;

3. 問題及解決方案

AVFoundation中提供了多層可用于播放的組件,例如AVPlayerViewControllerAVPlayer,這些系統組件可以滿足視頻的基本播放功能,在項目中,我們采用了AVPlayer進行播放預覽,但使用中遇到不少問題,后改用AVAssetReader做解碼,對解碼后的數據進行處理后做預覽。下面介紹播放預覽中系統組件的一些使用注意事項。

3.1 MediaToolBox使用注意事項

在使用AVPlayer做音頻變調、混響的預覽時,用到了MTAudioProcessingTap ,該類的所有回調是以函數指針存放于結構體中:

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;

OCC函數交互時,我們會在clientInfo變量中存放OC對象,在C語言函數的回調方法里使用__bridge的方式獲取OC對象。
OC對象釋放時,MTAudioProcessingTapcallback才返回,在回調的C函數里面獲取到的clientInfo就是野指針,crash就產生了。
clientInfo指向的對象被釋放時,需要保存已釋放的狀態,在回調里首先檢查該狀態,判斷當前對象是否釋放,以避免造成野指針訪問。

3.2 AVAssetReader使用注意事項

AVAssetReader可用于讀取AVAsset媒體資源的軌道數據,支持解碼、格式轉換、mix等操作。但注意事項也不少:

  1. AVAssetReader不可重復調用startReading,當出現failcomplete狀態后也不能重復調用;
  2. AVAssetReader做解碼的時候,切換后臺/來電會失去GPU權限,造成解碼失敗,AVAssetReader也變成fail狀態。異常打斷結束后,需要重啟reader,并確定reader重啟成功,否則需要retry
  3. AVAssetReader啟動后調用seek時,并不會很精準seek到目標點,一般會比指定的時間早幾幀(AVPlayer的精準seek,也有同樣的問題),需要記錄seek的目標時間點,如果seek后讀取出的buffer攜帶的 ptsseek的目標時間小,需要拋棄該數據;
  4. AVAssetReaderOutput不可重復添加,也不可在AssetReader調用startReading后添加;
  5. AVAssetReaderOutput不可在未添加前調用 copyNextSampleBuffer
  6. AVAssetReader釋放資源時,需要調用cancelReading來釋放 AVAsset資源,否則會出現fetch不到該資源的問題;
  7. AVAssetReader對文件視頻首幀非關鍵幀的視頻會解碼失敗,這說明AVAssetReader對文件格式要求很嚴格,不夠魯棒;
  8. AVAssetReader不支持m3u8文件,回出現讀取不到軌道信息的情況,如果需要解析HLS視頻,需要使用FFMpeg進行解封裝和VideoToolBox解碼;
  9. AVAssetReader內部創建了解碼器和緩存列表,但解碼器數量是有限制的(同AVPlayerItem)。

當然AVAssetReader 只做demux,不做解碼工作時可以避免上述一些問題,但需要自行使用VideoToolBox進行硬解,pixel format轉換也得單獨處理。

3.3 AudioQueue使用注意事項

AudioQueue可進行音頻播放,開播前會預緩存一定數量的buffer數據。在allocate buffer時,需要設置buffer的大小,該大小需要根據audio data format來設置,正常播放沒有問題,但播放速度非1.0的情況下,buffer太小時或太大,都會有異常的問題,需要考慮的有mBytesPerFramemChannelsPerFrame以及mSampleRate

而解碼后得到的音頻frame buffer中采樣數并不固定,當多音頻播放時,需要考慮是否存在audio data format變化的問題。
當然,每次切換音頻,重啟AudioQueue也是一種方案。

AudioQueue的數據獲取采用的是pull模式。在
AudioQueueOutputCallback的回調中,需要Enqueue待緩存的AudioQueueBufferRef
Enqueue的時候,可能會觸發AudioQueueStop或者AudioQueueDispose,盡管inImmediate設置為true,也會造成假死一段時間,需要在 AudioQueueOutputCallback的回調函數中先檢查狀態是否需要停止,如果為正常狀態,則Enqueue buffer,否則flush掉當前 AudioQueue的數據。

4. 結語

以上是iOS短視頻使用到的AVFoundation組件時遇到的問題,在 金山云多媒體SDK中硬編直接使用VideoToolBox做編碼,避免了一些AVAssetWriter的問題,此處未做贅述。
以上是遇到的一些問題,歡迎指正。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容