iOS 視訊處理框架及重點 API 合集丨音視訊工程示例

語言: CN / TW / HK

莫奈《睡蓮》

這個公眾號會 路線圖 式的遍歷分享音視訊技術 音視訊基礎(完成)  →  音視訊工具(完成)  →  音視訊工程示例(進行中)  →  音視訊工業實戰(準備) 關注一下成本不高,錯過乾貨損失不小 ↓↓↓

iOS/Android 客戶端開發同學如果想要開始學習音視訊開發,最絲滑的方式是對音視訊基礎概念知識有一定了解後,再借助 iOS/Android 平臺的音視訊能力上手去實踐音視訊的 採集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染 過程,並藉助音視訊工具來分析和理解對應的音視訊資料。

在音視訊工程示例這個欄目的 13 篇 AVDemo 文章中,我們拆解了 音訊和視訊採集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染 流程並基於 iOS 系統 API 實現了 Demo:

如果你看完這些 Demo,對 iOS 平臺的音視訊開發多多少少會有一些認識了。在 《iOS 音訊處理框架及重點 API 合集》 一文中,我們總結了一下 iOS 音訊處理框架以及音訊相關的 Demo 中用到的主要 API 和資料結構。

接下來,我們再來總結一下 iOS 視訊處理框架以及視訊相關的 Demo 中用到的主要 API 和資料結構。

1、iOS 視訊框架

當我們想要了解 iOS 的視訊處理框架時,以下是我們能比較容易找到的兩張官方架構圖。它們出自 AVFoundation Programming Guide [1] 這篇已經過陳舊過時的文件。

AVFoundation Stack on iOS
AVFoundation Stack on OS X

時至今日,iOS 平臺的視訊處理框架已經有了很多更新,上圖中很多在 OS X 上的模組也有了 iOS 版本的實現。雖然有變化,但是上面的架構圖,對我們瞭解現在 iOS 平臺的視訊處理框架還是有參考價值的。

根據我們的視訊 Demo 中涉及的系統 API,我們這裡挑選介紹幾個相關的 Framework:

  • Video Toolbox Framework

  • Core Media Framework

  • Core Video Framework

  • AVFoundation Framework

2、Video Toolbox Framework

Video Toolbox Framework [2] 主要用於支援視訊硬編碼和硬解碼。這裡我們主要介紹一下編碼和解碼相關的 API:

1) Data Compression [3] :通過一個編碼 Session 來管理對輸入視訊資料的壓縮操作。

  • VTCompressionSession [4] :編碼器 Session。

    • VTCompressionSessionCreate(...) [5] :建立編碼器 Session。

    • VTSessionSetProperty(...) [6] :設定編解碼器 Session 的屬性。

    • VTCompressionSessionPrepareToEncodeFrames(...) [7] :讓編碼器儘量初始化編碼需要的資源。這個方法呼叫是可選的,如果沒有呼叫這個方法,所有編碼需要的資源初始化會在第一次呼叫 VTCompressionSessionEncodeFrame(...) 時做。
    • VTCompressionSessionEncodeFrame(...) [8] :送資料給編碼器編碼。被編碼好的資料可能不會立即返回,所以在不要修改送給編碼器的 CVPixelBuffer
    • VTCompressionOutputCallback [9] :編碼資料回撥。

    • VTCompressionSessionCompleteFrames(...) [10] :強制編碼器完成所有或者指定時間點(completeUntilPresentationTimeStamp)及之前的所有幀。

    • VTCompressionSessionInvalidate(...) [11] :終止編碼。呼叫該方法後,記得用 CFRelease 釋放編碼器。

  • VTEncodeInfoFlags [12] :編碼時返回編碼操作相關資訊,可以在呼叫 VTCompressionSessionEncodeFrame(...) 等編碼介面時傳入,也會在編碼資料回撥中收到。有如下值:
    • kVTEncodeInfo_Asynchronous [13] :表示非同步編碼。

    • kVTEncodeInfo_FrameDropped [14] :表示該幀被丟棄。

  • VTIsHardwareDecodeSupported(...) [15] :當前系統和硬體是否支援指定編解碼器型別。

2) Data Decompression [16] :通過一個解碼 Session 來管理對輸入視訊資料的解壓縮操作。

  • VTDecompressionSession [17] :解碼器 Session。

    • VTDecompressionSessionCreate(...) [18] :建立解碼器 Session。解碼資料通過傳入的回撥(outputCallback)返回。

    • VTSessionSetProperty(...) [19] :設定編解碼器 Session 的屬性。

    • VTDecompressionSessionDecodeFrame(...) [20] :解碼送入的資料。

    • VTDecompressionOutputCallbackRecord [21] :解碼資料回撥。

    • VTDecompressionSessionFinishDelayedFrames(...) [22] :指示解碼器完成所有輸入資料的解碼。預設情況下,解碼器不會無限期的延遲解碼某一幀,除非該幀在輸入給解碼器時被設定了 kVTDecodeFrame_EnableTemporalProcessing 這個 VTDecodeFrameFlags。這個方法會在所有延遲的幀解碼輸出後返回,要等待它們,則需要呼叫 VTDecompressionSessionWaitForAsynchronousFrames(...)
    • VTDecompressionSessionWaitForAsynchronousFrames(...) [23] :等待所有非同步或延遲的幀解碼完成後再返回。呼叫這個方法後會自動呼叫 VTDecompressionSessionFinishDelayedFrames(...) ,所以使用方不用自己調。
    • VTDecompressionSessionInvalidate(...) [24] :終止解碼。呼叫該方法後,記得用 CFRelease 釋放解碼器。

  • VTDecodeInfoFlags [25] :解碼時返回解碼操作相關資訊,可以在呼叫 VTDecompressionSessionDecodeFrame(...) 等解碼介面時傳入,也會在解碼資料回撥中收到。有如下值:
    • kVTDecodeInfo_Asynchronous [26] :非同步解碼。

    • kVTDecodeInfo_FrameDropped [27] :幀被丟棄。

    • kVTDecodeInfo_ImageBufferModifiable [28] :可以修改 Buffer。

  • VTDecodeFrameFlags [29] :用於指導解碼器行為的指令。

    • kVTDecodeFrame_EnableAsynchronousDecompression [30] :告訴解碼器當前幀可以非同步解碼。但是非強制的。設定允許非同步解碼之後,解碼器會同時解碼幾幀資料,帶來的後果是,解碼總體時間更短,但是前面幾幀回撥的時間可能長一些。

    • kVTDecodeFrame_DoNotOutputFrame [31] :告訴解碼器不對該幀回撥解碼輸出資料,而是返回 NULL。某些情況我們不需要解碼器輸出幀,比如發生解碼器狀態錯誤的時候。

    • kVTDecodeFrame_1xRealTimePlayback(...) [32] :告訴解碼器可以使用低功耗模式解碼,設定之後處理器消耗會變少,解碼速度會變慢,通常我們不會設定這個引數,因為硬解碼使用的是專用處理器,不消耗 CPU,所以越快越好。

    • kVTDecodeFrame_EnableTemporalProcessing(...) [33] :通知解碼器需要處理幀序,設定之後解碼回撥會變慢,因為無論是非同步解碼還是 pts、dts 不相等的時候都需要進行幀排序,會耗時,蘋果官方文件不建議我們使用這個引數。

3、Core Media Framework

在前面介紹 iOS 音訊處理框架時,我們已經介紹過 Core Media Framework [34] 了,這個 Framework 中定義和封裝了 AVFoundation 等更上層的媒體框架需要的媒體處理流水線(包含時間資訊)以及其中使用的介面和資料型別。使用 Core Media 層的介面和資料型別可以高效的處理媒體取樣資料、管理取樣資料佇列。這裡,我們著重介紹一下其中跟視訊處理相關的部分。

1) Sample Processing [35] :取樣資料處理。常用的資料型別:

  • CMSampleBuffer [36] :系統用來在音視訊處理的 pipeline 中使用和傳遞媒體取樣資料的核心資料結構。你可以認為它是 iOS 音視訊處理 pipeline 中的流通貨幣,攝像頭採集的視訊資料介面、麥克風採集的音訊資料介面、編碼和解碼資料介面、讀取和儲存視訊介面、視訊渲染介面等等,都以它作為引數。通常,CMSampleBuffer 中要麼包含一個或多個媒體取樣的 CMBlockBuffer,要麼包含一個 CVImageBuffer(也作 CVPixelBuffer)。

    • kCMSampleAttachmentKey_NotSync [42] :Sync Sample 即 IDR 幀,可以用這個 key 對應的值來判斷當前幀是否是 IDR 幀,當對應的值為 kCFBooleanFalse 表示是 IDR 幀。這個屬性會被寫入媒體檔案或從媒體檔案中讀取。

    • kCMSampleAttachmentKey_PartialSync [43] :當前幀是否 Partial Sync Sample,Partial Sync Sample 可以不依賴前序幀就完成解碼(可認為是普通的 I 幀),兩個連續的 Partial Sync Sample 隨後的幀也可以不依賴這兩幀的前序的幀完成解碼。當設定一個幀為 Partial Sync Sample 時,需要同時設定 kCMSampleAttachmentKey_PartialSynckCMSampleAttachmentKey_NotSync 兩個屬性為 kCFBooleanTrue(可以認為是 I 幀,但是又區別於 IDR 幀)。這個屬性會被寫入媒體檔案或從媒體檔案中讀取。
    • kCMSampleAttachmentKey_DependsOnOthers [44] :當前幀是否依賴其他幀才能完成解碼。如果對應的值為 kCFBooleanTrue,表示依賴。比如,P 或 B 幀。這個屬性會被寫入媒體檔案或從媒體檔案中讀取。

    • kCMSampleAttachmentKey_IsDependedOnByOthers [45] :表示當前幀是否被其他幀依賴。如果對應的值為 kCFBooleanFalse,表示不被其他幀依賴,這時候是可以丟掉該幀的。這個屬性會被寫入媒體檔案或從媒體檔案中讀取。

    • kCMSampleAttachmentKey_DisplayImmediately [46] :如果對應的值為 kCFBooleanTrue,表示當前幀應該馬上渲染,即使還未到其 pts 時間。一般是在執行時來使用這個屬性來觸發一些渲染操作,比如 AVSampleBufferDisplayLayer 就可能用到。這個屬性不會被寫入媒體檔案。
    • kCMSampleAttachmentKey_DoNotDisplay [47] :表示當前幀是否只解碼不渲染。一般是在執行時來使用這個屬性來觸發一些渲染操作,比如 AVSampleBufferDisplayLayer 就可能用到。這個屬性不會被寫入媒體檔案。
    • kCMSampleAttachmentKey_EarlierDisplayTimesAllowed [48] :表示後面的幀是否有更早的顯示時間。

    • kCMSampleAttachmentKey_HasRedundantCoding [49] :表示當前幀是否有冗餘編碼。

    • CMSampleBufferGetFormatDescription(...) [37] :返回 CMSampleBuffer 中的取樣資料對應的 CMFormatDescription。

    • CMSampleBufferGetDataBuffer(...) [38] :返回 CMSampleBuffer 中的 CMBlockBuffer。注意呼叫方不會持有返回的 CMBlockBuffer,如果想要維護指向它的指標,需要顯式 retain 一下。

    • CMSampleBufferGetPresentationTimeStamp(...) [39] :獲取 CMSampleBuffer 中所有采樣的最小的 pts 時間戳。因為 CMSampleBuffer 中的取樣是按照解碼順序儲存的,展示順序可能與解碼順序一致,也可能不一致。

    • CMSampleBufferGetDecodeTimeStamp(...) [40] :獲取 CMSampleBuffer 中所有采樣的第一個取樣的 dts 時間戳。在 CMSampleBuffer 中,取樣是以解碼順序儲存的,即使與展示順序不一致。

    • CMSampleBufferGetSampleAttachmentsArray(...) [41] :獲取 CMSampleBuffer 中採用資料對應的附屬資料(attachment)陣列。這些附屬資料可能有下面這些 key:

  • CMBlockBuffer [50] :一個或多個媒體取樣的的裸資料。其中可以封裝:音訊採集後、編碼後、解碼後的資料(如:PCM 資料、AAC 資料);視訊編碼後的資料(如:H.264 資料)。

    • CMBlockBufferGetDataPointer(...) [51] :獲取訪問 CMBlockBuffer 中資料的地址。

  • CMFormatDescription [52] :用於描述 CMSampleBuffer 中取樣的格式資訊。

    • CMFormatDescriptionCreate(...) [53] :建立一個 CMFormatDescription。

    • CMVideoCodecType [54]typedef FourCharCode CMVideoCodecType ,視訊編碼型別。
    • CMMediaType [55]typedef FourCharCode CMMediaType ,媒體型別。
  • CMVideoFormatDescription [56]typedef CMFormatDescriptionRef CMVideoFormatDescriptionRef; CMVideoFormatDescription 是一種 CMFormatDescriptionRef。
    • CMVideoFormatDescriptionCreate(...) [57] :基於 CMVideoCodecType 來建立一個 CMVideoFormatDescription。

    • CMVideoFormatDescriptionGetDimensions(...) [58] :返回視訊編碼後的畫素尺寸 CMVideoDimensions

2) Time Representation [59] :時間資訊表示。常用的資料型別:

  • CMTime [60] :用 value/timescale 的方式表示時間。這樣可以解決浮點運算時的精度損失問題。timescale 表示時間刻度,通常在處理視訊內容時常見的時間刻度為 600,這是大部分常用視訊幀率 24fps、25fps、30fps 的公倍數,音訊資料常見的時間刻度就是取樣率,比如 44100 或 48000。

  • CMTimeRange [61] :用 start+duration 的方式表示一段時間。

  • CMSampleTimingInfo [62] :一個 CMSampleBuffer 的時間戳資訊,包括 pts、dts、duration。

3) Queues [63] :資料容器。常用的資料型別:

  • CMSimpleQueue [64] :一個簡單地、無鎖的 FIFO 佇列,可以放 (void *) 元素,元素不能是 NULL 或 0,如果元素是指向分配記憶體的指標,其記憶體生命週期要在外面自己管理。可以用作音視訊取樣資料(CMSampleBufferRef)的佇列,不過要自己加鎖。
  • CMBufferQueue [65] :支援儲存任何 CFTypeRef 型別的資料,但是資料型別需要有 duration 的概念,在建立 CMBufferQueue 的時候,會有一些回撥,其中一個必須的回撥是要返回佇列中物件的 duration。CMBufferQueue 是設計用於在生產者/消費者模型中在不同的執行緒中讀寫資料。通常是兩個執行緒(一個是生產者入隊執行緒,一個是消費者出隊執行緒),當然更多的執行緒也是可以的。

  • CMMemoryPool [66] :記憶體池容器,對使用大塊的記憶體有優化。一個 CMMemoryPool 的例項實際上維護一個最近釋放記憶體的池子用於記憶體分配服務。這樣的目的是加快隨後的記憶體分配。在需要重複分配大塊記憶體時,比如輸出視訊編碼資料,可以使用這個資料結構。

4、Core Video Framework

Core Video Framework [67] 主要用於支援數字視訊及數字影象幀的處理,提供基於處理 Pipeline 的 API,並且同時支援 Metal 和 OpenGL。

這裡我們主要介紹 CoreVideo Framework 中的幾種資料型別:

  • CVImageBuffer [68] :其中包含媒體流中 CMSampleBuffers 的格式描述、每個取樣的寬高和時序資訊、緩衝級別和取樣級別的附屬資訊。緩衝級別的附屬資訊是指緩衝區整體的資訊,比如播放速度、對後續緩衝資料的操作等。取樣級別的附屬資訊是指單個取樣的資訊,比如視訊幀的時間戳、是否關鍵幀等。其中可以封裝:視訊採集後、解碼後等未經編碼的資料(如:YCbCr 資料、RGBA 資料)。

  • CVPixelBuffer [69]typedef CVImageBufferRef CVPixelBufferRef ,畫素緩衝區。這是 iOS 平臺進行視訊編解碼及影象處理相關最重要的資料結構之一。它是在 CVImageBuffer 的基礎上實現了記憶體儲存。並且,CVPixelBuffer 還可以實現 CPU 和 GPU 共享記憶體,為影象處理提供更高的效率。
    • CVPixelBufferLockBaseAddress(...) [70] :鎖定 Pixel Buffer 的記憶體基地址。當使用 CPU 讀取 Pixel 資料時,需要讀取時鎖定,讀完解鎖。如果在鎖定時,帶了 kCVPixelBufferLock_ReadOnly 的 lockFlags,解鎖時也要帶上。但是,如果使用 GPU 讀取 Pixel 資料時,則沒有必要鎖定,反而會影響效能。
    • CVPixelBufferUnlockBaseAddress(...) [71] :解鎖 Pixel Buffer 的記憶體基地址。

    • CVPixelBufferGetBaseAddress(...) [72] :返回 Pixel Buffer 的記憶體基地址,但是根據 Buffer 的型別及建立場景的不同,返回的值的含義也有區別。對於 Chunky Buffers,返回的是座標 (0, 0) 畫素的記憶體地址;對於 Planar Buffers,返回的是對應的 CVPlanarComponentInfo 結構體的記憶體地址或者 NULL(如果不存在 CVPlanarComponentInfo 結構體的話),所以,對於 Planar Buffers,最好用 CVPixelBufferGetBaseAddressOfPlane(...)CVPixelBufferGetBytesPerRowOfPlane(...) 來獲取其中資料。獲取 Pixel Buffer 的基地址時,需要先用 CVPixelBufferLockBaseAddress(...) 加鎖。
    • CVPixelBufferGetHeight(...) [73] :返回 Buffer 的畫素高度。

    • CVPixelBufferGetWidth(...) [74] :返回 Buffer 的畫素寬度。

    • CVPixelBufferIsPlanar(...) [75] :判斷 Pixel Buffer 是否是 Planar 型別。

    • CVPixelBufferGetBaseAddressOfPlane(...) [76] :根據指定的 Plane Index 來獲取對應的基地址。獲取 Pixel Buffer 的基地址時,需要先用 CVPixelBufferLockBaseAddress(...) 加鎖。怎麼理解 Plane 呢?其實主要跟顏色模型有關,比如:儲存 YUV420P 時,有 Y、U、V 這 3 個 Plane;儲存 NV12 時,有 Y、UV 這 2 個 Plane;儲存 RGBA 時,有 R、G、B、A 這 4 個 Plane;而在 Packed 儲存模式中,因為所有分量的畫素是交織儲存的,所以只有 1 個 Plane。
    • CVPixelBufferGetPlaneCount(...) [77] :返回 Pixel Buffer 中 Plane 的數量。

    • CVPixelBufferGetBytesPerRowOfPlane(...) [78] :返回指定 Index 的 Plane 的每行位元組數。

    • CVPixelBufferGetHeightOfPlane(...) [79] :返回指定 Index 的 Plane 的高度。非 Planar 型別,返回 0。

    • CVPixelBufferGetWidthOfPlane(...) [80] :返回指定 Index 的 Plane 的寬度。非 Planar 型別,返回 0。

5、AVFoundation Framework

AVFoundation Framework [81] 是更上層的面向物件的一個音視訊處理框架。它提供了音視訊資源管理、相機裝置管理、音視訊處理、系統級音訊互動管理的能力,功能非常強大。如果對其功能進行細分,可以分為如下幾個模組:

  • Assets,音視訊資源管理。

  • Playback,媒體播放及自定義播放行為支援。

  • Capture,內建及外接的相機、麥克風等採集裝置管理,圖片、音視訊錄製。

  • Editing,音視訊編輯。

  • Audio,音訊播放、錄製和處理,App 系統音訊行為配置。

  • Speech,文字語音轉換。

在我們前面的 Demo 中封裝 Video Capture、Muxer、Demuxer 及設定 AudioSession 時會用到 AVFoundation Framework 的一些能力,我們這裡對應地介紹一下。

1)Video Capture

關於 iOS 視訊採集相關的架構,可以參考下面兩張圖:

AVCaptureSession 配置多組輸入輸出
AVCaptureConnection 連線單或多輸入和單輸出
  • AVCaptureDevice [82] :為音訊和視訊採集會話提供輸入的裝置,並且可以提供相關硬體裝置的控制能力,比如:攝像頭選擇、曝光、對焦、景深、縮放、閃光燈、夜景、幀率、白平衡、ISO、HDR、顏色空間、幾何失真等等。這裡不過多介紹,只介紹我們 Demo 中用到的一些介面:

    • -lockForConfiguration: [83] :在配置硬體相關的屬性時,需要先呼叫這個方法來鎖定。

    • -unlockForConfiguration: [84] :配置完後,解鎖。

    • -activeFormat [85] :屬性,獲取或設定當前採集裝置採集的媒體格式。

    • -activeVideoMinFrameDuration [86] :屬性,獲取或設定最低幀率。

    • -activeVideoMaxFrameDuration:屬性,獲取或設定最高幀率。

  • AVCaptureDeviceInput [87] :採集輸入,從採集裝置提供採集資料給採集會話。

  • AVCaptureDeviceFormat [88] :用於採集裝置的媒體格式或採集配置,比如視訊解析度、幀率等。

  • AVCaptureDevicePosition [89] :採集裝置物理位置。

  • AVCaptureSession [90] :採集會話。用於管理採集活動,協調採集資料在採集裝置和採集輸出物件之間的流轉。

    • -sessionPreset [91] :獲取或設定採集預設配置,設定採集輸出的質量級別或位元速率。

    • -addInput: [92] :為採集會話新增輸入。

    • -addOutput: [93] :為採集會話新增輸出。

    • -addConnection: [94] :為採集會話新增連線物件。

  • AVCaptureSessionPreset [95] :採集預設配置,表示採集輸出的質量級別或位元速率。比如:AVCaptureSessionPresetLow、AVCaptureSessionPresetHigh、AVCaptureSessionPreset1280x720、AVCaptureSessionPreset1920x1080 等。

  • AVCaptureSessionRuntimeErrorNotification [96] :採集會話是否發生錯誤的通知。

  • AVCaptureConnection [97] :在採集會話中連線一對採集輸入和輸出。可以設定採集視訊映象、防抖等。

    • -videoMirrored [98] :經過 Connection 的視訊是否映象。

    • -preferredVideoStabilizationMode [99] :設定採集影象防抖模式。

  • AVCaptureVideoDataOutput [100] :採集視訊的輸出物件。提供訪問視訊幀進行影象處理的能力,可以通過 -captureOutput:didOutputSampleBuffer:fromConnection: 回撥獲取採集的資料。
    • -setSampleBufferDelegate:queue: [101] :設定採集視訊的回撥和任務佇列。

    • -captureOutput:didOutputSampleBuffer:fromConnection::採集視訊輸出資料回撥。

    • -alwaysDiscardsLateVideoFrames [102] :採集視訊輸出時,當幀到的太晚是否丟棄。預設 YES。如果設定 NO,會給 -captureOutput:didOutputSampleBuffer:fromConnection: 更多時間處理幀,但是這時候記憶體佔用可能會更大。
  • AVCaptureVideoPreviewLayer [103] :可以用來渲染採集視訊資料的 Core Animation Layer。

    • -setVideoGravity: [104] :設定渲染的內容填充模式。

2)Muxer

  • AVAssetWriter [105] :支援將媒體資料寫入 QuickTime 或 MPEG-4 格式的檔案中,支援對多軌道的媒體資料進行交錯處理來提高播放和儲存的效率,支援對媒體取樣進行轉碼,支援寫入 metadata。需要注意的是,一個 AVAssetWriter 例項只能對應寫一個檔案,如果要寫入多個檔案,需要建立多個 AVAssetWriter 例項。

    • canAddInput: [106] :檢查 AVAssetWriter 是否支援新增對應的 AVAssetWriterInput。

    • addInput: [107] :給 AVAssetWriter 新增一個 AVAssetWriterInput。注意必須在 AVAssetWriter 開始寫入之前新增。

    • startWriting [108] :開始寫入。必須在配置好 AVAssetWriter 新增完 AVAssetWriterInput 做好準備後再呼叫這個方法。在呼叫完這個方法後,需要呼叫 startSessionAtSourceTime: 開始寫入會話,此後就可以使用對應的 AVAssetWriterInput 來寫入媒體取樣資料。
    • startSessionAtSourceTime: [109] :開啟寫入會話。在 startWriting 後呼叫,在寫入媒體取樣資料之前呼叫。
    • endSessionAtSourceTime: [110] :結束寫入會話。結束時間是會話結束時樣本資料在時間軸上的時刻。如果沒有顯示呼叫這個方法,系統會在你呼叫 finishWritingWithCompletionHandler: 結束寫入時自動呼叫。
    • finishWritingWithCompletionHandler: [111] :標記 AVAssetWriter 的所有 input 為結束,完成寫入。為了保證 AVAssetWriter 完成所有采樣資料的寫入,要在呼叫新增資料正確返回後呼叫這個方法。

    • cancelWriting [112] :取消建立輸出檔案。如果 AVAssetWriter 的狀態是 Failed 或 Completed,呼叫這個方法無效,否則,呼叫它會阻塞呼叫執行緒,直到會話取消完成。如果 AVAssetWriter 已經建立了輸出檔案,呼叫這個方法會刪除這個檔案。

  • AVAssetWriterInput [113] :用於向 AVAssetWriter 例項的輸出檔案的一個軌道新增媒體取樣資料。一個例項只能對應一個軌道媒體資料或 metadata 資料的寫入,當使用多個例項向多個軌道寫入資料時,需要注意檢查 AVAssetWriterInput 的 readyForMoreMediaData 屬性。

    • expectsMediaDataInRealTime [114] :輸入是否為實時資料來源,比如相機採集。當設定這個值為 YES 時,會優化用於實時使用的輸入來精準計算 readyForMoreMediaData 的狀態。
    • readyForMoreMediaData [115] :表示 AVAssetWriterInput 是否已經準備好接收媒體資料。

    • requestMediaDataWhenReadyOnQueue:usingBlock: [116] :告訴 AVAssetWriterInput 在方便的時候去請求資料並寫入輸出檔案。在對接拉取式的資料來源時,可以用這個方法。

    • appendSampleBuffer: [117] :通過 AVAssetWriterInput 向輸出檔案新增媒體資料,但是新增之前媒體資料的順序需要自己處理。注意,呼叫這個方法新增取樣資料後,不要更改取樣資料的內容。

    • markAsFinished [118] :標記 AVAssetWriterInput 為完成,表示已經完成向它新增媒體資料了。

3)Demuxer

  • AVAssetReader [119] :用於從 AVAsset 資源中讀取媒體資料。這個 AVAsset 可以是 QuickTime 或 MPEG-4 檔案,也可以是編輯創作的 AVComposition。

    • canAddOutput: [120] :檢查 AVAssetReader 是否支援新增對應的 AVAssetReaderOutput。

    • addOutput: [121] :給 AVAssetReader 新增一個 AVAssetReaderOutput。注意必須在 AVAssetReader 開始讀取之前新增。

    • startReading [122] :開始讀取。

    • cancelReading [123] :在讀完資料之前取消讀取可以呼叫這個介面。

  • AVAssetReaderOutput [124] :一個抽象類,定義了從 AVAsset 資源中讀取媒體取樣資料的介面。通常我們可以使用 AVAssetReaderTrackOutputAVAssetReaderVideoCompositionOutput 等具體的實現類。
  • AVAssetReaderTrackOutput [125]

    • alwaysCopiesSampleData [126] :是否總是拷貝取樣資料。如果要修改讀取的取樣資料,可以設定 YES,否則就設定 NO,這樣效能會更好。

    • copyNextSampleBuffer [127] :從 Output 拷貝下一個 CMSampleBuffer。

  • AVAudioSession [128] :在最新版本的 iOS 系統庫中,AVAudioSession 已經遷移到 AVFAudio Framework 中了。AVAudioSession 是系統用來管理 App 對音訊硬體資源的使用的,比如:設定當前 App 與其他 App 同時使用音訊時,是否混音、打斷或降低其他 App 的聲音;手機靜音鍵開啟時是否還可以播放聲音;指定音訊輸入或者輸出裝置;是否支援錄製或邊錄製邊播放;聲音被打斷時的通知。我們這裡只簡單介紹下 Demo 中用到的介面:

    • setCategory:withOptions:error: [129] :設定 AudioSession 的型別和選項引數。比如型別為 AVAudioSessionCategoryPlayback 表示支援播放;AVAudioSessionCategoryPlayAndRecord 表示同時支援播放和錄製等等。

    • setMode:error: [130] :設定 AudioSession 的模式。AudioSession 的型別和模式一起決定了 App 如何使用音訊。通常需要在啟用 AudioSession 之前設定型別和模式。比如模式為 AVAudioSessionModeVideoRecording 表示當期要錄製視訊;AVAudioSessionModeVoiceChat 表示語音聊天。

    • setActive:withOptions:error: [131] :啟用或釋放 AudioSession 的使用。

以上這些框架及 API 基本上可以覆蓋我們在前面的 Demo 中用到的能力了。

參考資料

[1]

AVFoundation Programming Guide: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html

[2]

Video Toolbox Framework: https://developer.apple.com/documentation/videotoolbox?language=objc

[3]

Data Compression: https://developer.apple.com/documentation/videotoolbox?language=objc

[4]

VTCompressionSession: https://developer.apple.com/documentation/videotoolbox/vtcompressionsession?language=objc

[5]

VTCompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1428285-vtcompressionsessioncreate?language=objc

[6]

VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc

[7]

VTCompressionSessionPrepareToEncodeFrames(...): https://developer.apple.com/documentation/videotoolbox/1428283-vtcompressionsessionpreparetoenc?language=objc

[8]

VTCompressionSessionEncodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1428287-vtcompressionsessionencodeframe?language=objc

[9]

VTCompressionOutputCallback: https://developer.apple.com/documentation/videotoolbox/vtcompressionoutputcallback?language=objc

[10]

VTCompressionSessionCompleteFrames(...): https://developer.apple.com/documentation/videotoolbox/1428303-vtcompressionsessioncompletefram?language=objc

[11]

VTCompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1428295-vtcompressionsessioninvalidate?language=objc

[12]

VTEncodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags?language=objc

[13]

kVTEncodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_asynchronous?language=objc

[14]

kVTEncodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_framedropped?language=objc

[15]

VTIsHardwareDecodeSupported(...): https://developer.apple.com/documentation/videotoolbox/2887343-vtishardwaredecodesupported?language=objc

[16]

Data Decompression: https://developer.apple.com/documentation/videotoolbox?language=objc

[17]

VTDecompressionSession: https://developer.apple.com/documentation/videotoolbox/vtdecompressionsession?language=objc

[18]

VTDecompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1536134-vtdecompressionsessioncreate?language=objc

[19]

VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc

[20]

VTDecompressionSessionDecodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1536071-vtdecompressionsessiondecodefram?language=objc

[21]

VTDecompressionOutputCallbackRecord: https://developer.apple.com/documentation/videotoolbox/vtdecompressionoutputcallbackrecord?language=objc

[22]

VTDecompressionSessionFinishDelayedFrames(...): https://developer.apple.com/documentation/videotoolbox/1536101-vtdecompressionsessionfinishdela?language=objc

[23]

VTDecompressionSessionWaitForAsynchronousFrames(...): https://developer.apple.com/documentation/videotoolbox/1536066-vtdecompressionsessionwaitforasy?language=objc

[24]

VTDecompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1536093-vtdecompressionsessioninvalidate?language=objc

[25]

VTDecodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags?language=objc

[26]

kVTDecodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_asynchronous?language=objc

[27]

kVTDecodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_framedropped?language=objc

[28]

kVTDecodeInfo_ImageBufferModifiable: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_imagebuffermodifiable?language=objc

[29]

VTDecodeFrameFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags?language=objc

[30]

kVTDecodeFrame_EnableAsynchronousDecompression: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enableasynchronousdecompression?language=objc

[31]

kVTDecodeFrame_DoNotOutputFrame: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_donotoutputframe?language=objc

[32]

kVTDecodeFrame_1xRealTimePlayback(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_1xrealtimeplayback?language=objc

[33]

kVTDecodeFrame_EnableTemporalProcessing(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enabletemporalprocessing?language=objc

[34]

Core Media: https://developer.apple.com/documentation/coremedia?language=objc

[35]

Sample Processing: https://developer.apple.com/documentation/coremedia?language=objc

[36]

CMSampleBuffer: https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc

[37]

CMSampleBufferGetFormatDescription(...): https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc

[38]

CMSampleBufferGetDataBuffer(...): https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc

[39]

CMSampleBufferGetPresentationTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc

[40]

CMSampleBufferGetDecodeTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489404-cmsamplebuffergetdecodetimestamp?language=objc

[41]

CMSampleBufferGetSampleAttachmentsArray(...): https://developer.apple.com/documentation/coremedia/1489189-cmsamplebuffergetsampleattachmen?language=objc

[42]

kCMSampleAttachmentKey_NotSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_notsync?language=objc

[43]

kCMSampleAttachmentKey_PartialSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_partialsync?language=objc

[44]

kCMSampleAttachmentKey_DependsOnOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_dependsonothers?language=objc

[45]

kCMSampleAttachmentKey_IsDependedOnByOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_isdependedonbyothers?language=objc

[46]

kCMSampleAttachmentKey_DisplayImmediately: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_displayimmediately?language=objc

[47]

kCMSampleAttachmentKey_DoNotDisplay: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_donotdisplay?language=objc

[48]

kCMSampleAttachmentKey_EarlierDisplayTimesAllowed: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_earlierdisplaytimesallowed?language=objc

[49]

kCMSampleAttachmentKey_HasRedundantCoding: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_hasredundantcoding?language=objc

[50]

CMBlockBuffer: https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc

[51]

CMBlockBufferGetDataPointer(...): https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc

[52]

CMFormatDescription: https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc

[53]

CMFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc

[54]

CMVideoCodecType: https://developer.apple.com/documentation/coremedia/cmvideocodectype?language=objc

[55]

CMMediaType: https://developer.apple.com/documentation/coremedia/1564193-cmmediatype/

[56]

CMVideoFormatDescription: https://developer.apple.com/documentation/coremedia/cmvideoformatdescription?language=objc

[57]

CMVideoFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489743-cmvideoformatdescriptioncreate?language=objc

[58]

CMVideoFormatDescriptionGetDimensions(...): https://developer.apple.com/documentation/coremedia/1489287-cmvideoformatdescriptiongetdimen?language=objc

[59]

Time Representation: https://developer.apple.com/documentation/coremedia?language=objc

[60]

CMTime: https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc

[61]

CMTimeRange: https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc

[62]

CMSampleTimingInfo: https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc

[63]

Queues: https://developer.apple.com/documentation/coremedia?language=objc

[64]

CMSimpleQueue: https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc

[65]

CMBufferQueue: https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc

[66]

CMMemoryPool: https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc

[67]

Core Video Framework: https://developer.apple.com/documentation/corevideo?language=objc

[68]

CVImageBuffer: https://developer.apple.com/documentation/corevideo/cvimagebuffer-q40

[69]

CVPixelBuffer: https://developer.apple.com/documentation/corevideo/cvpixelbuffer?language=objc

[70]

CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457128-cvpixelbufferlockbaseaddress?language=objc

[71]

CVPixelBufferUnlockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1456843-cvpixelbufferunlockbaseaddress?language=objc

[72]

CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457115-cvpixelbuffergetbaseaddress?language=objc

[73]

CVPixelBufferGetHeight(...): https://developer.apple.com/documentation/corevideo/1456666-cvpixelbuffergetheight?language=objc

[74]

CVPixelBufferGetWidth(...): https://developer.apple.com/documentation/corevideo/1457241-cvpixelbuffergetwidth?language=objc

[75]

CVPixelBufferIsPlanar(...): https://developer.apple.com/documentation/corevideo/1456805-cvpixelbufferisplanar?language=objc

[76]

CVPixelBufferGetBaseAddressOfPlane(...): https://developer.apple.com/documentation/corevideo/1456821-cvpixelbuffergetbaseaddressofpla?language=objc

[77]

CVPixelBufferGetPlaneCount(...): https://developer.apple.com/documentation/corevideo/1456976-cvpixelbuffergetplanecount?language=objc

[78]

CVPixelBufferGetBytesPerRowOfPlane(...): https://developer.apple.com/documentation/corevideo/1456711-cvpixelbuffergetbytesperrowofpla?language=objc

[79]

CVPixelBufferGetHeightOfPlane(...): https://developer.apple.com/documentation/corevideo/1456698-cvpixelbuffergetheightofplane?language=objc

[80]

CVPixelBufferGetWidthOfPlane(...): https://developer.apple.com/documentation/corevideo/1456830-cvpixelbuffergetwidthofplane?language=objc

[81]

AVFoundation Framework: https://developer.apple.com/documentation/avfoundation?language=objc

[82]

AVCaptureDevice: https://developer.apple.com/documentation/avfoundation/avcapturedevice?language=objc

[83]

-lockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387810-lockforconfiguration?language=objc

[84]

-unlockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387917-unlockforconfiguration?language=objc

[85]

-activeFormat: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389221-activeformat?language=objc

[86]

-activeVideoMinFrameDuration: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389290-activevideominframeduration?language=objc

[87]

AVCaptureDeviceInput: https://developer.apple.com/documentation/avfoundation/avcapturedeviceinput?language=objc

[88]

AVCaptureDeviceFormat: https://developer.apple.com/documentation/avfoundation/avcapturedeviceformat?language=objc

[89]

AVCaptureDevicePosition: https://developer.apple.com/documentation/avfoundation/avcapturedeviceposition?language=objc

[90]

AVCaptureSession: https://developer.apple.com/documentation/avfoundation/avcapturesession?language=objc

[91]

-sessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389696-sessionpreset?language=objc

[92]

-addInput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387239-addinput?language=objc

[93]

-addOutput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387325-addoutput?language=objc

[94]

-addConnection:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389687-addconnection?language=objc

[95]

AVCaptureSessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesessionpreset?language=objc

[96]

AVCaptureSessionRuntimeErrorNotification: https://developer.apple.com/documentation/avfoundation/avcapturesessionruntimeerrornotification?language=objc

[97]

AVCaptureConnection: https://developer.apple.com/documentation/avfoundation/avcaptureconnection?language=objc

[98]

-videoMirrored: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1389172-videomirrored?language=objc

[99]

preferredVideoStabilizationMode: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1620484-preferredvideostabilizationmode?language=objc

[100]

AVCaptureVideoDataOutput: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput?language=objc

[101]

-setSampleBufferDelegate:queue:: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1389008-setsamplebufferdelegate?language=objc

[102]

-alwaysDiscardsLateVideoFrames: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1385780-alwaysdiscardslatevideoframes?language=objc

[103]

AVCaptureVideoPreviewLayer: https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer?language=objc

[104]

-setVideoGravity:: https://developer.apple.com/documentation/watchkit/wkinterfacemovie/1628130-setvideogravity?language=objc

[105]

AVAssetWriter: https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc

[106]

canAddInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc

[107]

addInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc

[108]

startWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc

[109]

startSession(atSourceTime:): https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc

[110]

endSessionAtSourceTime:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc

[111]

finishWritingWithCompletionHandler:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc

[112]

cancelWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc

[113]

AVAssetWriterInput: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc

[114]

expectsMediaDataInRealTime: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc

[115]

readyForMoreMediaData: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc

[116]

requestMediaDataWhenReadyOnQueue:usingBlock:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc

[117]

appendSampleBuffer:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc

[118]

markAsFinished: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc

[119]

AVAssetReader: https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc

[120]

canAddOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc

[121]

addOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc

[122]

startReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc

[123]

cancelReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc

[124]

AVAssetReaderOutput: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc

[125]

AVAssetReaderTrackOutput: https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc

[126]

alwaysCopiesSampleData: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc

[127]

copyNextSampleBuffer: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc

[128]

AVAudioSession: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc

[129]

setCategory:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc

[130]

setMode:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc

[131]

setActive:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc

- 完 -

加我微信,拉你入群

謝謝看完全文,也點一下『贊』和 『在看』吧 ↓