iOS 視訊處理框架及重點 API 合集丨音視訊工程示例
莫奈《睡蓮》
這個公眾號會 路線圖 式的遍歷分享音視訊技術 : 音視訊基礎(完成) → 音視訊工具(完成) → 音視訊工程示例(進行中) → 音視訊工業實戰(準備) 。 關注一下成本不高,錯過乾貨損失不小 ↓↓↓
iOS/Android 客戶端開發同學如果想要開始學習音視訊開發,最絲滑的方式是對音視訊基礎概念知識有一定了解後,再借助 iOS/Android 平臺的音視訊能力上手去實踐音視訊的 採集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
過程,並藉助音視訊工具來分析和理解對應的音視訊資料。
在音視訊工程示例這個欄目的 13 篇 AVDemo 文章中,我們拆解了 音訊和視訊 的 採集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
流程並基於 iOS 系統 API 實現了 Demo:
-
音訊 Demo 合集:
-
視訊 Demo 合集:
如果你看完這些 Demo,對 iOS 平臺的音視訊開發多多少少會有一些認識了。在 《iOS 音訊處理框架及重點 API 合集》 一文中,我們總結了一下 iOS 音訊處理框架以及音訊相關的 Demo 中用到的主要 API 和資料結構。
接下來,我們再來總結一下 iOS 視訊處理框架以及視訊相關的 Demo 中用到的主要 API 和資料結構。
1、iOS 視訊框架
當我們想要了解 iOS 的視訊處理框架時,以下是我們能比較容易找到的兩張官方架構圖。它們出自 AVFoundation Programming Guide [1] 這篇已經過陳舊過時的文件。


時至今日,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_PartialSync
和kCMSampleAttachmentKey_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 視訊採集相關的架構,可以參考下面兩張圖:


-
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 資源中讀取媒體取樣資料的介面。通常我們可以使用
AVAssetReaderTrackOutput
、AVAssetReaderVideoCompositionOutput
等具體的實現類。 -
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 中用到的能力了。
參考資料
AVFoundation Programming Guide: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html
Video Toolbox Framework: https://developer.apple.com/documentation/videotoolbox?language=objc
Data Compression: https://developer.apple.com/documentation/videotoolbox?language=objc
VTCompressionSession: https://developer.apple.com/documentation/videotoolbox/vtcompressionsession?language=objc
VTCompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1428285-vtcompressionsessioncreate?language=objc
VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc
VTCompressionSessionPrepareToEncodeFrames(...): https://developer.apple.com/documentation/videotoolbox/1428283-vtcompressionsessionpreparetoenc?language=objc
VTCompressionSessionEncodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1428287-vtcompressionsessionencodeframe?language=objc
VTCompressionOutputCallback: https://developer.apple.com/documentation/videotoolbox/vtcompressionoutputcallback?language=objc
VTCompressionSessionCompleteFrames(...): https://developer.apple.com/documentation/videotoolbox/1428303-vtcompressionsessioncompletefram?language=objc
VTCompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1428295-vtcompressionsessioninvalidate?language=objc
VTEncodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags?language=objc
kVTEncodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_asynchronous?language=objc
kVTEncodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_framedropped?language=objc
VTIsHardwareDecodeSupported(...): https://developer.apple.com/documentation/videotoolbox/2887343-vtishardwaredecodesupported?language=objc
Data Decompression: https://developer.apple.com/documentation/videotoolbox?language=objc
VTDecompressionSession: https://developer.apple.com/documentation/videotoolbox/vtdecompressionsession?language=objc
VTDecompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1536134-vtdecompressionsessioncreate?language=objc
VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc
VTDecompressionSessionDecodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1536071-vtdecompressionsessiondecodefram?language=objc
VTDecompressionOutputCallbackRecord: https://developer.apple.com/documentation/videotoolbox/vtdecompressionoutputcallbackrecord?language=objc
VTDecompressionSessionFinishDelayedFrames(...): https://developer.apple.com/documentation/videotoolbox/1536101-vtdecompressionsessionfinishdela?language=objc
VTDecompressionSessionWaitForAsynchronousFrames(...): https://developer.apple.com/documentation/videotoolbox/1536066-vtdecompressionsessionwaitforasy?language=objc
VTDecompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1536093-vtdecompressionsessioninvalidate?language=objc
VTDecodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags?language=objc
kVTDecodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_asynchronous?language=objc
kVTDecodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_framedropped?language=objc
kVTDecodeInfo_ImageBufferModifiable: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_imagebuffermodifiable?language=objc
VTDecodeFrameFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags?language=objc
kVTDecodeFrame_EnableAsynchronousDecompression: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enableasynchronousdecompression?language=objc
kVTDecodeFrame_DoNotOutputFrame: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_donotoutputframe?language=objc
kVTDecodeFrame_1xRealTimePlayback(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_1xrealtimeplayback?language=objc
kVTDecodeFrame_EnableTemporalProcessing(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enabletemporalprocessing?language=objc
Core Media: https://developer.apple.com/documentation/coremedia?language=objc
Sample Processing: https://developer.apple.com/documentation/coremedia?language=objc
CMSampleBuffer: https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc
CMSampleBufferGetFormatDescription(...): https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc
CMSampleBufferGetDataBuffer(...): https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc
CMSampleBufferGetPresentationTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc
CMSampleBufferGetDecodeTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489404-cmsamplebuffergetdecodetimestamp?language=objc
CMSampleBufferGetSampleAttachmentsArray(...): https://developer.apple.com/documentation/coremedia/1489189-cmsamplebuffergetsampleattachmen?language=objc
kCMSampleAttachmentKey_NotSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_notsync?language=objc
kCMSampleAttachmentKey_PartialSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_partialsync?language=objc
kCMSampleAttachmentKey_DependsOnOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_dependsonothers?language=objc
kCMSampleAttachmentKey_IsDependedOnByOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_isdependedonbyothers?language=objc
kCMSampleAttachmentKey_DisplayImmediately: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_displayimmediately?language=objc
kCMSampleAttachmentKey_DoNotDisplay: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_donotdisplay?language=objc
kCMSampleAttachmentKey_EarlierDisplayTimesAllowed: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_earlierdisplaytimesallowed?language=objc
kCMSampleAttachmentKey_HasRedundantCoding: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_hasredundantcoding?language=objc
CMBlockBuffer: https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc
CMBlockBufferGetDataPointer(...): https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc
CMFormatDescription: https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc
CMFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc
CMVideoCodecType: https://developer.apple.com/documentation/coremedia/cmvideocodectype?language=objc
CMMediaType: https://developer.apple.com/documentation/coremedia/1564193-cmmediatype/
CMVideoFormatDescription: https://developer.apple.com/documentation/coremedia/cmvideoformatdescription?language=objc
CMVideoFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489743-cmvideoformatdescriptioncreate?language=objc
CMVideoFormatDescriptionGetDimensions(...): https://developer.apple.com/documentation/coremedia/1489287-cmvideoformatdescriptiongetdimen?language=objc
Time Representation: https://developer.apple.com/documentation/coremedia?language=objc
CMTime: https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc
CMTimeRange: https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc
CMSampleTimingInfo: https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc
Queues: https://developer.apple.com/documentation/coremedia?language=objc
CMSimpleQueue: https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc
CMBufferQueue: https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc
CMMemoryPool: https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc
Core Video Framework: https://developer.apple.com/documentation/corevideo?language=objc
CVImageBuffer: https://developer.apple.com/documentation/corevideo/cvimagebuffer-q40
CVPixelBuffer: https://developer.apple.com/documentation/corevideo/cvpixelbuffer?language=objc
CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457128-cvpixelbufferlockbaseaddress?language=objc
CVPixelBufferUnlockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1456843-cvpixelbufferunlockbaseaddress?language=objc
CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457115-cvpixelbuffergetbaseaddress?language=objc
CVPixelBufferGetHeight(...): https://developer.apple.com/documentation/corevideo/1456666-cvpixelbuffergetheight?language=objc
CVPixelBufferGetWidth(...): https://developer.apple.com/documentation/corevideo/1457241-cvpixelbuffergetwidth?language=objc
CVPixelBufferIsPlanar(...): https://developer.apple.com/documentation/corevideo/1456805-cvpixelbufferisplanar?language=objc
CVPixelBufferGetBaseAddressOfPlane(...): https://developer.apple.com/documentation/corevideo/1456821-cvpixelbuffergetbaseaddressofpla?language=objc
CVPixelBufferGetPlaneCount(...): https://developer.apple.com/documentation/corevideo/1456976-cvpixelbuffergetplanecount?language=objc
CVPixelBufferGetBytesPerRowOfPlane(...): https://developer.apple.com/documentation/corevideo/1456711-cvpixelbuffergetbytesperrowofpla?language=objc
CVPixelBufferGetHeightOfPlane(...): https://developer.apple.com/documentation/corevideo/1456698-cvpixelbuffergetheightofplane?language=objc
CVPixelBufferGetWidthOfPlane(...): https://developer.apple.com/documentation/corevideo/1456830-cvpixelbuffergetwidthofplane?language=objc
AVFoundation Framework: https://developer.apple.com/documentation/avfoundation?language=objc
AVCaptureDevice: https://developer.apple.com/documentation/avfoundation/avcapturedevice?language=objc
-lockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387810-lockforconfiguration?language=objc
-unlockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387917-unlockforconfiguration?language=objc
-activeFormat: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389221-activeformat?language=objc
-activeVideoMinFrameDuration: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389290-activevideominframeduration?language=objc
AVCaptureDeviceInput: https://developer.apple.com/documentation/avfoundation/avcapturedeviceinput?language=objc
AVCaptureDeviceFormat: https://developer.apple.com/documentation/avfoundation/avcapturedeviceformat?language=objc
AVCaptureDevicePosition: https://developer.apple.com/documentation/avfoundation/avcapturedeviceposition?language=objc
AVCaptureSession: https://developer.apple.com/documentation/avfoundation/avcapturesession?language=objc
-sessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389696-sessionpreset?language=objc
-addInput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387239-addinput?language=objc
-addOutput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387325-addoutput?language=objc
-addConnection:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389687-addconnection?language=objc
AVCaptureSessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesessionpreset?language=objc
AVCaptureSessionRuntimeErrorNotification: https://developer.apple.com/documentation/avfoundation/avcapturesessionruntimeerrornotification?language=objc
AVCaptureConnection: https://developer.apple.com/documentation/avfoundation/avcaptureconnection?language=objc
-videoMirrored: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1389172-videomirrored?language=objc
preferredVideoStabilizationMode: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1620484-preferredvideostabilizationmode?language=objc
AVCaptureVideoDataOutput: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput?language=objc
-setSampleBufferDelegate:queue:: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1389008-setsamplebufferdelegate?language=objc
-alwaysDiscardsLateVideoFrames: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1385780-alwaysdiscardslatevideoframes?language=objc
AVCaptureVideoPreviewLayer: https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer?language=objc
-setVideoGravity:: https://developer.apple.com/documentation/watchkit/wkinterfacemovie/1628130-setvideogravity?language=objc
AVAssetWriter: https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc
canAddInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc
addInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc
startWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc
startSession(atSourceTime:): https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc
endSessionAtSourceTime:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc
finishWritingWithCompletionHandler:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc
cancelWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc
AVAssetWriterInput: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
expectsMediaDataInRealTime: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc
readyForMoreMediaData: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc
requestMediaDataWhenReadyOnQueue:usingBlock:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
appendSampleBuffer:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc
markAsFinished: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc
AVAssetReader: https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc
canAddOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc
addOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc
startReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc
cancelReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc
AVAssetReaderOutput: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc
AVAssetReaderTrackOutput: https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc
alwaysCopiesSampleData: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc
copyNextSampleBuffer: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc
AVAudioSession: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
setCategory:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc
setMode:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc
setActive:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
- 完 -
謝謝看完全文,也點一下『贊』和 『在看』吧 ↓
- 一文看完 WWDC 2022 音視訊相關的更新要點丨音視訊工程示例
- 一看就懂的 OpenGL 基礎概念丨音視訊基礎
- 視訊轉碼後有色差要如何處理呢?丨有問有答
- WWDC 2022 音視訊相關 Session 概覽(EDR 相關)丨音視訊工程示例
- 音視訊知識圖譜 2022.06
- Android AVDemo(13):視訊渲染丨音視訊工程示例
- 想在自己的視訊平臺支援 HDR 需要做哪些工作?丨有問有答
- Android AVDemo(11):視訊轉封裝,從 MP4 到 MP4丨音視訊工程示例
- 音視訊面試題集錦 2022.05
- Android AVDemo(6):音訊渲染,免費獲得原始碼丨音視訊工程示例
- Android AVDemo(4):音訊解封裝,從 MP4 中解封裝出 AAC丨音視訊工程示例
- 如何根據 NALU 裸流資料來判斷其是 H.264 還是 H.265 編碼?丨有問有答
- 音視訊知識圖譜 2022.04
- Android AVDemo(2):音訊編碼,採集 PCM 資料編碼為 AAC丨音視訊工程示例
- 音視訊面試題集錦 2022.04
- Android AVDemo(1):音訊採集,免費獲取全部原始碼丨音視訊工程示例
- iOS 視訊處理框架及重點 API 合集丨音視訊工程示例
- iOS AVDemo(13):視訊渲染,用 Metal 渲染丨音視訊工程示例
- 如何像抖音直播一樣,從 App 直播間到桌面畫中畫實現畫面無縫切換?丨有問有答
- 如何在視訊採集流水線中增加濾鏡處理節點?丨有問有答