[轉]AVFoundation 框架小結

AVFoundation 小結

概述

AVFoundation 是 Objective-C 中建立及編輯視聽媒體文件的幾個框架之一,其提供了檢查、建立、編輯或從新編碼媒體文件的接口,也使得從設備獲取的視頻實時數據可操縱。可是,一般狀況,簡單的播放或者錄像,直接使用 AVKit 框架或者 UIImagePickerController 類便可。另外,值得注意的是,在 AVFoundation 框架中使用的基本數據結構,如時間相關的或描述媒體數據的數據結構都聲明在 CoreMedia 框架中。css

  1. AVFoundation 框架包含視頻相關的接口以及音頻相關的接口,與音頻相關的類有 AVAudioPlayer、AVAudioRecorder、AVAudioSession。java

  2. AVFoundation 框架中最基本的類是 AVAsset ,它是一個或者多個媒體數據的集合,描述的是整個集合的屬性,如標題、時長、大小等,而且沒有特定的數據格式。集合的每個媒體數據都是統一的數據類型,稱之爲 track。簡單的狀況是一種數據是音頻數據,一種是視頻數據,而較複雜的狀況是一種數據交織着音頻和視頻數據,而且 AVAsset 是可能有元數據的。算法

    另外,須要明白的是在 AVFoundation 中,初始化了 asset 及 track 後,並不意味着資源已經可用,由於若資源自己並不攜帶自身信息時,那麼系統須要本身計算相關信息,這個過程會阻塞線程,因此應該使用異步方式進行獲取資源信息後的操做。數組

  3. AVFoundation 提供了豐富的方法來管理視聽資源的播放,爲了支持這些方法,它將描述 asset 的狀態與 asset 自己分離,這就使得在同一個時刻,以不一樣的方式播放同一個 asset 中的不一樣的媒體數據變得可能。對於 asset 的狀態是由 player 管理的,而 asset 中的 track 的狀態是由 player tracker 管理的。使用這兩個狀態管理對象,能夠實現諸如設置 asset 中視頻部分的大小、設置音頻的混合參數及與視頻的合成或者將 asset 中的某些媒體數據置爲不可用。緩存

    另外,還能夠經過 player 將輸出定位到 Core Animation 層中,或經過播放隊列設置 player 集合的播放順序。ruby

  4. AVFoundation 提供了多種方法來建立 asset ,能夠簡單的重編碼已經存在的 asset ,這個過程可使用 export session 或者使用 asset reader 和 asset writer 。markdown

  5. 若要生成視頻的縮略圖,可使用 asset 初始化一個 AVAssetImageGenerator 實例對象,它會使用默承認用的視頻 tracks 來生成圖片。cookie

  6. AVFoundation 中可使用 compositions 將多個媒體數據(video/audio tracks)合成爲一個 asset ,這個過程當中,能夠添加或移除 tracks ,調整它們的順序,或者設置音頻的音量和變化坡度,視頻容量等屬性。這些媒體數據的集合保存在內存中,直到使用 export session 將它導出到本地文件中。另外,還可使用 asset writer 建立 asset 。網絡

  7. 使用 capture session 協調從設備(如相機、麥克風)輸入的數據和輸出目標(如視頻文件)。能夠爲 session 設置多個輸入和輸出,即便它正在工做,還能夠經過它中止數據的流動。另外,還可使用 preview layer 將相機記錄的影像實時展現給用戶。session

  8. 在 AVFoundation 中的回調處理並不保證回調任務在某個特定的線程或隊列中執行,其遵循兩個原則,UI 相關的操做在主線程中執行,其餘回調須要爲其指定調用的隊列。

基本類

AVAsset

建立 AVAsset 或其子類 AVURLAsset 時,須要提供資源的位置,方法以下:

NSURL *url = <#視聽資源的 URL ,能夠是本地文件地址,也能夠是網頁媒體連接#>; AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

上述方法的第二個參數是建立對象時的選擇項,其中可能包含的選擇項以下:

  • AVURLAssetPreferPreciseDurationAndTimingKey 是否須要資源的準確時長,及訪問資源各個準確的時間點
  • AVURLAssetReferenceRestrictionsKey 連接其餘資源的約束
  • AVURLAssetHTTPCookiesKey 添加資源可以訪問的 HTTP cookies
  • AVURLAssetAllowsCellularAccessKey 是否可以使用蜂窩網絡

建立並初始化一個 AVAsset 實例對象後,並不意味着該對象的全部屬性均可以獲取使用了,由於其中的一些屬性須要額外的計算纔可以獲得,那麼當獲取這些屬性時,可能會阻塞當前線程,因此須要異步獲取這些屬性。

AVAsset 與 AVAssetTrack 都遵循 AVAsynchronousKeyValueLoading 協議,這個協議中有如下兩個方法:

//獲取指定屬性的狀態 - (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError; //異步加載指定的屬性集合 - (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;

一般,咱們使用上述第二個方法異步加載想要的屬性,然後在加載完成的回調 block 中使用第一個方法判斷屬性是否加載成功,而後訪問想要的屬性,執行本身的操做,以下代碼:

NSURL *url = <#資源路徑#>; AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil]; NSArray *keys = @[@"duration",@"tracks"]; [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() { NSError *error = nil; AVKeyValueStatus tracksStatus = [asset statusOfValueForKey:@"tracks" error:&error]; //根據相應的屬性狀態進行對應的處理 switch (tracksStatus) { case AVKeyValueStatusUnknown: //TODO break; case AVKeyValueStatusLoading: //TODO break; case AVKeyValueStatusLoaded: //TODO break; case AVKeyValueStatusFailed: //TODO break; case AVKeyValueStatusCancelled: //TODO break; } }];

AVAssetImageGenerator

使用 AVAssetImageGenerator 生成視頻資源的縮略圖,使用 AVAsset 對象建立 AVAssetImageGenerator 對象,可使用類方法或實例方法,以下:

+ (instancetype)assetImageGeneratorWithAsset:(AVAsset *)asset; - (instancetype)initWithAsset:(AVAsset *)asset NS_DESIGNATED_INITIALIZER;

固然,在此以前,最好調用 AVAsset 中的方法 - (NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic; 來判斷 asset 中是否有可視媒體數據。若是有,那麼再建立 AVAssetImageGenerator 對象,然後再調用下面的方法,來獲取一張或多張圖片。

//獲取一張圖片,requestedTime 指定要獲取視頻中哪一個時刻的圖片,actualTime 返回圖片實際是視頻的哪一個時刻,outError 返回錯誤信息 - (nullable CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(nullable CMTime *)actualTime error:(NSError * _Nullable * _Nullable)outError CF_RETURNS_RETAINED; //獲取多張圖片,每一次圖片生成後,都會調用一次 handler - (void)generateCGImagesAsynchronouslyForTimes:(NSArray<NSValue *> *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler; //上述 handler 的類型以下,回調中的參數有圖片的請求時刻和實際時刻,圖片,狀態(成功、失敗、取消),錯誤信息 typedef void (^AVAssetImageGeneratorCompletionHandler)(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error);

AVAssetExportSession

使用 AVAssetExportSession 類對視頻進行裁剪及轉碼,即將一個 AVAsset 類實例修改後保存爲另外一個 AVAsset 類實例,最後保存到文件中。

在修改資源以前,爲避免不兼容帶來的錯誤,能夠先調用下面的方法,檢查預設置是否合理。

//獲取與 asset 兼容的預設置 + (NSArray<NSString *> *)exportPresetsCompatibleWithAsset:(AVAsset *)asset; //判斷提供的預設置和輸出的文件類型是否與 asset 相兼容 + (void)determineCompatibilityOfExportPreset:(NSString *)presetName withAsset:(AVAsset *)asset outputFileType:(nullable NSString *)outputFileType completionHandler:(void (^)(BOOL compatible))handler NS_AVAILABLE(10_9, 6_0);

除了設置文件類型外,還能夠設置文件的大小、時長、範圍等屬性,一切準備就緒後,調用方法:

- (void)exportAsynchronouslyWithCompletionHandler:(void (^)(void))handler;

進行文件的導出,導出結束後,會調用 handler 回調,在回調中應該檢查 AVAssetExportSession 的 status 屬性查看導出是否成功,若指定的文件保存地址在沙盒外,或在導出的過程當中有電話打入都會致使文件保存失敗,以下例程:

- (void)exportVideo:(NSURL *)url { AVAsset *anAsset = [AVAsset assetWithURL:url]; [AVAssetExportSession determineCompatibilityOfExportPreset:AVAssetExportPresetHighestQuality withAsset:anAsset outputFileType:AVFileTypeMPEG4 completionHandler:^(BOOL compatible) { if (compatible){ AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:anAsset presetName:AVAssetExportPresetHighestQuality]; exportSession.outputFileType = AVFileTypeMPEG4; CMTime start = CMTimeMakeWithSeconds(1.0, 600); CMTime duration = CMTimeMakeWithSeconds(3.0, 600); CMTimeRange range = CMTimeRangeMake(start, duration); exportSession.timeRange = range; [exportSession exportAsynchronouslyWithCompletionHandler:^{ switch ([exportSession status]) { case AVAssetExportSessionStatusCompleted: NSLog(@"completed"); break; case AVAssetExportSessionStatusFailed: NSLog(@"failed"); break; case AVAssetExportSessionStatusCancelled: NSLog(@"canceled"); break; default: break; } }]; } }]; }

媒體資源播放

使用一個 AVPlayer 類實例能夠管理一個 asset 資源,可是它的屬性 currentItem 纔是 asset 的實際管理者。currentItem 是 AVPlayerItem 類的實例,而它的屬性 tracks 包含着的 AVPlayerItemTracker 實例對應着 asset 中的各個 track 。

那麼,爲了控制 asset 的播放,可使用 AVPlayer 類,在播放的過程當中,可使用 AVPlayerItem 實例管理整個 asset 的狀態,使用 AVPlayerItemTracker 對象管理 asset 中每一個 track 的狀態。另外,還可使用 AVPlayerLayer 類來顯示播放的內容。

因此,在建立 AVPlayer 實例對象時,除了能夠直接傳遞資源文件的路徑進行建立外,還能夠傳遞 AVPlayerItem 的實例對象,以下方法:

+ (instancetype)playerWithURL:(NSURL *)URL; + (instancetype)playerWithPlayerItem:(nullable AVPlayerItem *)item; - (instancetype)initWithURL:(NSURL *)URL; - (instancetype)initWithPlayerItem:(nullable AVPlayerItem *)item;

建立後,並非能夠直接使用,還要對它的狀態進行檢查,只有 status 的值爲 AVPlayerStatusReadyToPlay 時,才能進行播放,因此這裏須要使用 KVO 模式對該狀態進行監控,以決定什麼時候能夠進行播放。

若要管理多個資源的播放,則應使用 AVPlayer 的子類 AVQueuePlayer ,這個子類擁有的多個 AVPlayerItem 同各個資源相對應。

不一樣類型的 asset

對於播放不一樣類型的資源,須要進行的準備工做有所不一樣,這主要取決於資源的來源。資源數據可能來自本地設備上文件的讀取,也可能來自網絡上數據流。

對於本地文件,可使用文件地址建立 AVAsset 對象,然後使用該對象建立 AVPlayerItem 對象,最後將這個 item 對象與 AVPlayer 對象相關聯。以後,即是等待 status 的狀態變爲 AVPlayerStatusReadyToPlay ,即可以進行播放了。

對於網絡數據的播放,不能使用地址建立 AVAsset 對象了,而是直接建立 AVPlayerItem 對象,將其同 AVPlayer 對象相關聯,當 status 狀態變爲 AVPlayerStatusReadyToPlay 後,AVAsset 和 AVAssetTrack 對象將由 item 對象建立。

播放控制

經過調用 player 的 play 、pause 、setRate: 方法,能夠控制 item 的播放,這些方法都會改變 player 的屬性 rate 的值,該值爲 1 表示 item 按正常速率播放,爲 0 表示 item 暫停播放,0~1 表示低速播放,大於 1 表示高速播放,小於 0 表示從後向前播放。

item 的屬性 timeControlStatus 的值表示當前 item 的狀態,有下面 3 個值:

  • AVPlayerTimeControlStatusPaused 暫停
  • AVPlayerTimeControlStatusPlaying 播放
  • AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate 等待按指定速率播放狀態,該狀態是當 rate 的值設置爲非 0 值時,而 item 因某些緣由還沒法播放的狀況,而沒法播放的緣由,可依經過 item 的 reasonForWaitingToPlay 屬性值查看。

item 的屬性 actionAtItemEnd 的值表示當前 item 播放結束後的動做,有下面 3 個值:

  • AVPlayerActionAtItemEndAdvance 只適用於 AVQueuePlayer 類,表示播放隊列中的下一個 item
  • AVPlayerActionAtItemEndPause 表示暫停
  • AVPlayerActionAtItemEndNone 表示無操做,當前 item 的 currentTime 屬性值仍然按 rate 的值改變

item 的 currentTime 屬性值表示當前 item 的播放時間,能夠調用下面的方法指定 item 從何處進行播放。

//第二個方法可以進行更準確的跳轉,可是須要進行額外的計算
- (void)seekToDate:(NSDate *)date; - (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter; (tolerance: 公差,先後公差) //這兩個方法傳入了一個回調,當一個時間跳轉請求被新的請求或其餘操做打斷時,回調也會被執行可是此時 finished 參數值爲 NO - (void)seekToTime:(CMTime)time completionHandler:(void (^)(BOOL finished))completionHandler NS_AVAILABLE(10_7, 5_0); - (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL finished))completionHandler NS_AVAILABLE(10_7, 5_0);

使用 AVQueuePlayer 管理多個 item 的播放,仍然能夠經過調用 play 開始依次播放 item,調用 advanceToNextItem 方法播放下一個 item ,還能夠經過下面的方法添加或移除 item 。

- (BOOL)canInsertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem; - (void)insertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem; - (void)removeItem:(AVPlayerItem *)item; - (void)removeAllItems;

可使用下面的方法監聽播放時間的變化,須要強引用這兩個方法返回的監聽者。

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block; - (id)addBoundaryTimeObserverForTimes:(NSArray<NSValue *> *)times queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(void))block;

用上面的方法每註冊一個監聽者,就須要對應的使用下面的方法進行註銷,而且在註銷以前,要確保沒有 block 被執行。

- (void)removeTimeObserver:(id)observer;

當 item 播放結束後,再次調用 player 的方法 play 不會使 item 從新播放,要實現重播,能夠註冊一個 AVPlayerItemDidPlayToEndTimeNotification 通知,當接收到這個通知時,能夠調 seekToTime: 方法,傳入 kCMTimeZero 參數,將 player 的播放時間重置。

媒體資源編輯基本類

AVFoundation 框架中提供了豐富的接口用於視聽資源的編輯,其中的關鍵是 composition ,它將不一樣的 asset 相結合並造成一個新的 asset 。使用 AVMutableComposition 類能夠增刪 asset 來將指定的 asset 集合到一塊兒。除此以外,若想將集合到一塊兒的視聽資源以自定義的方式進行播放,須要使用 AVMutableAudioMix 和 AVMutableVideoComposition類對其中的資源進行協調管理。最終要使用 AVAssetExportSession 類將編輯的內容保存到文件中。

AVComposition

同 AVAsset 擁有多個 AVAssetTrack 同樣,做爲子類的 AVComposition 也擁有多個 AVCompositionTrack ,而 AVCompositionTrack 是 AVAssetTrack 的子類。因此,AVComposition 實例對象是多個 track 的集合,真正描述媒體屬性的是 AVCompositionTrack 實例對象。而 AVCompositionTrack 又是媒體數據片斷的集合,這些數據片斷由 AVCompositionTrackSegment 類進行描述。

該類的相關屬性和方法以下:

//獲取 composition 中包含的 tracks @property (nonatomic, readonly) NSArray<AVCompositionTrack *> *tracks; //獲取 composition 中可視媒體資源播放時在屏幕上顯示的大小 @property (nonatomic, readonly) CGSize naturalSize; //獲取 composition 生成 asset 時的指定配置 @property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *URLAssetInitializationOptions NS_AVAILABLE(10_11, 9_0); //根據不一樣的參數,獲取 composition 中的 track - (nullable AVCompositionTrack *)trackWithTrackID:(CMPersistentTrackID)trackID; - (NSArray<AVCompositionTrack *> *)tracksWithMediaType:(NSString *)mediaType; - (NSArray<AVCompositionTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;

值得注意的是 AVComposition 類中並無提供初始化方法,通常咱們使用它的子類 AVMutableComposition ,進行各類操做後,再生成 AVComposition 實例以供查詢,以下例程:

AVMutableComposition *mutableComposition = [AVMutableComposition composition]; //進行添加資源等操做 <#····#> //使用可變的 composition 生成一個不可變的 composition 以供使用 AVComposition *composition = [myMutableComposition copy]; AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:composition];

AVMutableComposition

AVMutableComposition 是 AVComposition 的子類,其包含的 tracks 則是 AVCompositionTrack 的子類 AVMutableCompositionTrack 。

AVMutableComposition 中提供了兩個類方法用來獲取一個空的 AVMutableComposition 實例對象。

+ (instancetype)composition;
+ (instancetype)compositionWithURLAssetInitializationOptions:(nullable NSDictionary<NSString *, id> *)URLAssetInitializationOptions NS_AVAILABLE(10_11, 9_0);

對整個 composition 中的 tracks 的修改方法以下:

//將指定時間段的 asset 中的全部的 tracks 添加到 composition 中 startTime 處 //該方法可能會在 composition 中添加新的 track 以便 asset 中 timeRange 範圍中的全部 tracks 都添加到 composition 中 - (BOOL)insertTimeRange:(CMTimeRange)timeRange ofAsset:(AVAsset *)asset atTime:(CMTime)startTime error:(NSError * _Nullable * _Nullable)outError; //向 composition 中的全部 tracks 添加空的時間範圍 - (void)insertEmptyTimeRange:(CMTimeRange)timeRange; //從 composition 的全部 tracks 中刪除一段時間,該操做不會刪除 track ,而是會刪除與該時間段相交的 track segment - (void)removeTimeRange:(CMTimeRange)timeRange; //改變 composition 中的全部的 tracks 的指定時間範圍的時長,該操做會改變 asset 的播放速度 - (void)scaleTimeRange:(CMTimeRange)timeRange toDuration:(CMTime)duration;

從 composition 中獲取 track 或向其中添加/移除 track 方法以下:

//向 composition 中添加一個空的 track ,而且指定媒體資源類型及 trackID 屬性值 //若提供的參數 preferredTrackID 無效或爲 kCMPersistentTrackID_Invalid ,那麼惟一的 trackID 會自動生成 - (AVMutableCompositionTrack *)addMutableTrackWithMediaType:(NSString *)mediaType preferredTrackID:(CMPersistentTrackID)preferredTrackID; //從 composition 中刪除一個指定的 track - (void)removeTrack:(AVCompositionTrack *)track; //獲取一個與 asset track 相兼容的 composition track //爲了更好的性能,composition track 的數量應保持最小,這個數量與必需並行播放的媒體數據段數量以及媒體數據的類型相關 //對於可以線性執行且類型相同的媒體數據應使用同一個 composition track ,即便這些數據來自不一樣的 asset - (nullable AVMutableCompositionTrack *)mutableTrackCompatibleWithTrack:(AVAssetTrack *)track;

AVMutableComposition 中也提供了過濾 AVMutableCompositionTrack 的接口

- (nullable AVMutableCompositionTrack *)trackWithTrackID:(CMPersistentTrackID)trackID; - (NSArray<AVMutableCompositionTrack *> *)tracksWithMediaType:(NSString *)mediaType; - (NSArray<AVMutableCompositionTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;

AVCompositionTrack

AVCompositionTrack 類同其父類 AVAssetTrack 同樣是媒體資源的管理者,它實際是媒體資源數據的集合,它的屬性 segments 是 AVCompositionTrackSegment 類的實例對象集合,每一個對象描述一個媒體數據片斷。類 AVCompositionTrack 並不經常使用,一般使用的是它的子類 AVMutableCompositionTrack 。

AVMutableCompositionTrack

AVMutableCompositionTrack 中提供的屬性以下:

//沒有外部數值指定時,媒體1秒鐘時間的粒度 @property (nonatomic) CMTimeScale naturalTimeScale; //當前 track 相關聯的語言編碼 @property (nonatomic, copy, nullable) NSString *languageCode; //當前 track 相關聯的額外語言編碼 @property (nonatomic, copy, nullable) NSString *extendedLanguageTag; //對於可顯示的媒體數據應優先選擇的仿射變換設置,默認值爲 CGAffineTransformIdentity @property (nonatomic) CGAffineTransform preferredTransform; //應優先選擇的音量,默認值爲 1 @property (nonatomic) float preferredVolume; //當前track 所包含的全部的媒體數據片斷,對於這些片斷,它們構成了 track 的完整時間線, //因此他們的時間線不能夠重疊,而且第一個數據片斷的時間從 kCMTimeZero 開始,依次日後的時間必須連續不間斷、不重疊 @property (nonatomic, copy, null_resettable) NSArray<AVCompositionTrackSegment *> *segments;

當咱們獲取了一個 AVMutableCompositionTrack 實例對象後,即可以經過如下方法對其進行添加或移除數據片斷

//將已存在的資源文件指定時間範圍的媒體數據插入到當前 composition 的指定時間處
//若是 startTime 爲 kCMTimeInvalid 值,那麼數據被添加到 composition 的最後 - (BOOL)insertTimeRange:(CMTimeRange)timeRange ofTrack:(AVAssetTrack *)track atTime:(CMTime)startTime error:(NSError * _Nullable * _Nullable)outError; //這個方法與上述方法相似,只是能夠批量操做,可是注意提供的時間範圍不能重疊 - (BOOL)insertTimeRanges:(NSArray<NSValue *> *)timeRanges ofTracks:(NSArray<AVAssetTrack *> *)tracks atTime:(CMTime)startTime error:(NSError * _Nullable * _Nullable)outError NS_AVAILABLE(10_8, 5_0); //插入一個沒有媒體數據的時間段,當這個範圍以前的媒體資源播放結束後,不會馬上播放以後的媒體數據,而是會靜默一段時間 - (void)insertEmptyTimeRange:(CMTimeRange)timeRange; //移除一段時間範圍的媒體數據,該方法不會致使該 track 從 composition 中移除,只是移除與時間範圍相交的數據片斷 - (void)removeTimeRange:(CMTimeRange)timeRange; //改變某個時間範圍內的時間的時長,實質是改變了媒體數據的播放速率 //其速率是原時長與現時長的比值,總之,媒體數據是要按時長播放的 - (void)scaleTimeRange:(CMTimeRange)timeRange toDuration:(CMTime)duration; //判斷數據片斷的時間線是否重疊 - (BOOL)validateTrackSegments:(NSArray<AVCompositionTrackSegment *> *)trackSegments error:(NSError * _Nullable * _Nullable)outError;

AVAssetTrackSegment

媒體資源 AVAsset 中的集合 AVAssetTrack 管理着單條時間線上的媒體數據片斷,而每一個數據片斷則由 AVAssetTrackSegment 類進行描述。

AVAssetTrackSegment 有兩個屬性

  • timeMapping 描述的是數據片斷在整個媒體文件中所處的時間範圍

    timeMapping 是一個結構體,擁有兩個成員,對於編輯中的媒體數據片斷,它們分別表示數據在源文件中的位置和目標文件中的位置
    typedef struct { CMTimeRange source; CMTimeRange target; } CMTimeMapping;
  • empty 描述該數據片斷是否爲空,若是爲空,其 timeMapping.source.start 爲 kCMTimeInvalid

AVCompositionTrackSegment

在編輯媒體文件時,在描述數據時,使用的是 AVAssetTrackSegment 的子類 AVCompositionTrackSegment ,她的主要屬性和方法以下:

//判斷數據片斷是否爲空,若爲空 timeMapping.target 可爲有效值,其餘爲未定義值 @property (nonatomic, readonly, getter=isEmpty) BOOL empty; //片斷數據所處的文件的地址 @property (nonatomic, readonly, nullable) NSURL *sourceURL; //片斷數據所處文件的描述 asset track 的 ID @property (nonatomic, readonly) CMPersistentTrackID sourceTrackID; //建立對象,提供了數據片斷所在的文件、文件的描述 asset track 的 ID 、源文件中的數據時間範圍、目標文件中所處的時間範圍 //sourceTimeRange 與 targetTimeRange 的時間長度若是不一致,那麼播放的速率會改變 + (instancetype)compositionTrackSegmentWithURL:(NSURL *)URL trackID:(CMPersistentTrackID)trackID sourceTimeRange:(CMTimeRange)sourceTimeRange targetTimeRange:(CMTimeRange)targetTimeRange; - (instancetype)initWithURL:(NSURL *)URL trackID:(CMPersistentTrackID)trackID sourceTimeRange:(CMTimeRange)sourceTimeRange targetTimeRange:(CMTimeRange)targetTimeRange NS_DESIGNATED_INITIALIZER; //建立僅有時間範圍而無實際媒體數據的實例 + (instancetype)compositionTrackSegmentWithTimeRange:(CMTimeRange)timeRange; - (instancetype)initWithTimeRange:(CMTimeRange)timeRange NS_DESIGNATED_INITIALIZER; 

音頻的自定義播放

要在媒體資源播放的過程當中實現音頻的自定義播放,須要用 AVMutableAudioMix 對不一樣的音頻進行編輯。這個類的實例對象的屬性 inputParameters 是音量描述對象的集合,每一個對象都是對一個 audio track 的音量變化的描述,以下示例:

AVMutableAudioMix *mutableAudioMix = [AVMutableAudioMix audioMix];

AVMutableAudioMixInputParameters *mixParameters1 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTrack1];
[mixParameters1 setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero, mutableComposition.duration/2)]; [mixParameters1 setVolumeRampFromStartVolume:0.f toEndVolume:1.f timeRange:CMTimeRangeMake(mutableComposition.duration/2, mutableComposition.duration)]; AVMutableAudioMixInputParameters *mixParameters2 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTrack2]; [mixParameters2 setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero, mutableComposition.duration)]; mutableAudioMix.inputParameters = @[mixParameters1, mixParameters2];

AVAudioMix

該類中有一個屬性 inputParameters ,它是 AVAudioMixInputParameters 實例對象的集合,每一個實例都是對音頻播放方式的描述。可見,AVAudioMix 並不直接改變音頻播放的方式,其只是存儲了音頻播放的方式。

AVMutableAudioMix

AVMutableAudioMix 是 AVAudioMix 的子類,它的方法 audioMix 返回一個 inputParameters 屬性爲空的實例。

AVAudioMixInputParameters

這個類是音量變化的描述類,它同一個音頻的 track 相關聯,並設置音量隨時間變化的算法,其獲取音量變化的方法以下:

//獲取的音量變化範圍 timeRange 應包含指定的時刻 time 不然最終返回 NO //startVolume 獲取音量開始變化時的初始音量 //endVolume 獲取音量變化結束時的音量 //timeRang 是實際音量變化的範圍,它應該包含指定的 time - (BOOL)getVolumeRampForTime:(CMTime)time startVolume:(nullable float *)startVolume endVolume:(nullable float *)endVolume timeRange:(nullable CMTimeRange *)timeRange;

AVMutableAudioMixInputParameters

AVMutableAudioMixInputParameters 是 AVAudioMixInputParameters 的子類,它提供了直接設置某個時刻或時間段的音量的方法。

//根據提供的 track 建立一個實例,此時的音量描述數據爲空 + (instancetype)audioMixInputParametersWithTrack:(nullable AVAssetTrack *)track; //建立一個實例,此時的音量變化描述是空的,且 trackID 爲 kCMPersistentTrackID_Invalid + (instancetype)audioMixInputParameters; //設置某個時間範圍內的初始音量及結束音量 - (void)setVolumeRampFromStartVolume:(float)startVolume toEndVolume:(float)endVolume timeRange:(CMTimeRange)timeRange; //設置某個時刻的音量 - (void)setVolume:(float)volume atTime:(CMTime)time;

視頻的自定義播放

同音頻的自定義播放同樣,要實現視頻的自定義播放,僅僅將視頻資源集合到一塊兒是不夠的,須要使用 AVMutableVideoComposition 類來定義不一樣的視頻資源在不一樣的時間範圍內的播放方式。

AVVideoComposition

AVVideoComposition 是 AVMutableVideoComposition 的父類,它的主要屬性和方法以下:

//該類的構造類,提供自定義的構造類時,提供的類要遵照 AVVideoCompositing 協議 @property (nonatomic, readonly, nullable) Class<AVVideoCompositing> customVideoCompositorClass NS_AVAILABLE(10_9, 7_0); //視頻每一幀的刷新時間 @property (nonatomic, readonly) CMTime frameDuration; //視頻顯示時的大小範圍 @property (nonatomic, readonly) CGSize renderSize; //視頻顯示範圍大小的縮放比例(僅僅對 iOS 有效) @property (nonatomic, readonly) float renderScale; //描述視頻集合中具體視頻播放方式信息的集合,其是遵循 AVVideoCompositionInstruction 協議的類實例對象 //這些視頻播放信息構成一個完整的時間線,不能重疊,不能間斷,而且在數組中的順序即爲相應視頻的播放順序 @property (nonatomic, readonly, copy) NSArray<id <AVVideoCompositionInstruction>> *instructions; //用於組合視頻幀與動態圖層的 Core Animation 的工具對象,能夠爲 nil @property (nonatomic, readonly, retain, nullable) AVVideoCompositionCoreAnimationTool *animationTool; //直接使用一個 asset 建立一個實例,建立的實例的各個屬性會根據 asset 中的全部的 video tracks 的屬性進行計算並適配,因此在調用該方法以前,確保 asset 中的屬性已經加載 //返回的實例對象的屬性 instructions 中的對象會對應每一個 asset 中的 track 中屬性要求 //返回的實例對象的屬性 frameDuration 的值是 asset 中 全部 track 的 nominalFrameRate 屬性值最大的,若是這些值都爲 0 ,默認爲 30fps //返回的實例對象的屬性 renderSize 的值是 asset 的 naturalSize 屬性值,若是 asset 是 AVComposition 類的實例。不然,renderSize 的值將包含每一個 track 的 naturalSize 屬性值 + (AVVideoComposition *)videoCompositionWithPropertiesOfAsset:(AVAsset *)asset NS_AVAILABLE(10_9, 6_0); //這三個屬性設置了渲染幀時的顏色空間、矩陣、顏色轉換函數,可能的值都在 AVVideoSetting.h 文件中定義 @property (nonatomic, readonly, nullable) NSString *colorPrimaries NS_AVAILABLE(10_12, 10_0); @property (nonatomic, readonly, nullable) NSString *colorYCbCrMatrix NS_AVAILABLE(10_12, 10_0); @property (nonatomic, readonly, nullable) NSString *colorTransferFunction NS_AVAILABLE(10_12, 10_0); //該方法返回一個實例,它指定的 block 會對 asset 中每個有效的 track 的每一幀進行渲染獲得 CIImage 實例對象 //在 block 中進行每一幀的渲染,成功後應調用 request 的方法 finishWithImage:context: 並將獲得的 CIImage 對象做爲參數 //如果渲染失敗,則應調用 finishWithError: 方法並傳遞錯誤信息 + (AVVideoComposition *)videoCompositionWithAsset:(AVAsset *)asset applyingCIFiltersWithHandler:(void (^)(AVAsynchronousCIImageFilteringRequest *request))applier NS_AVAILABLE(10_11, 9_0);

AVMutableVideoComposition

AVMutableVideoComposition 是 AVVideoComposition 的可變子類,它繼承父類的屬性能夠改變,而且新增了下面的建立方法。

//這個方法建立的實例對象的屬性的值都是 nil 或 0,可是它的屬性都是能夠進行修改的 + (AVMutableVideoComposition *)videoComposition;

AVVideoCompositionInstruction

在上述的兩個類中,真正包含有視頻播放方式信息的是 instructions 屬性,這個集合中的對象都遵循 AVVideoCompositionInstruction 協議,若不使用自定義的類,那麼可使用 AVFoundation 框架中的 AVVideoCompositionInstruction 類。

該類的相關屬性以下:

//表示該 instruction 生效的時間範圍 @property (nonatomic, readonly) CMTimeRange timeRange; //指定當前時間段的 composition 的背景色 //若是沒有指定,那麼使用默認的黑色 //若是渲染的像素沒有透明度通道,那麼這個顏色也會忽略透明度 @property (nonatomic, readonly, retain, nullable) __attribute__((NSObject)) CGColorRef backgroundColor; //AVVideoCompositionLayerInstruction 類實例對象的集合,描述各個視頻資源幀的層級及組合關係 //按這個數組的順序,第一個顯示在第一層,第二個在第一層下面顯示,以此類推 @property (nonatomic, readonly, copy) NSArray<AVVideoCompositionLayerInstruction *> *layerInstructions; //代表該時間段的視頻幀是否須要後期處理 //若爲 NO,後期圖層的處理將跳過該時間段,這樣可以提升效率 //爲 YES 則按默認操做處理(參考 AVVideoCompositionCoreAnimationTool 類) @property (nonatomic, readonly) BOOL enablePostProcessing; //當前 instruction 中須要進行幀組合的全部的 track ID 的集合,由屬性 layerInstructions 計算獲得 @property (nonatomic, readonly) NSArray<NSValue *> *requiredSourceTrackIDs NS_AVAILABLE(10_9, 7_0); //若是當前的 instruction 在該時間段內的視頻幀組合後,實質獲得的是某個源視頻的幀,那麼就返回這個視頻資源的 ID @property (nonatomic, readonly) CMPersistentTrackID passthroughTrackID NS_AVAILABLE(10_9, 7_0); 

AVMutableVideoCompositionInstruction

AVMutableVideoCompositionInstruction 是 AVVideoCompositionInstruction 的子類,其繼承的父類的屬性可進行修改,而且提供了建立屬性值爲 nil 或無效的實例的方法。

+ (instancetype)videoCompositionInstruction;

AVVideoCompositionLayerInstruction

AVVideoCompositionLayerInstruction 是對給定的視頻資源的不一樣播放方式進行描述的類,經過下面的方法,能夠獲取仿射變化、透明度變化、裁剪區域變化的梯度信息。

//獲取包含指定時間的仿射變化梯度信息 //startTransform、endTransform 用來接收變化過程的起始值與結束值 //timeRange 用來接收變化的持續時間範圍 //返回值表示指定的時間 time 是否在變化時間 timeRange 內 - (BOOL)getTransformRampForTime:(CMTime)time startTransform:(nullable CGAffineTransform *)startTransform endTransform:(nullable CGAffineTransform *)endTransform timeRange:(nullable CMTimeRange *)timeRange; //獲取包含指定時間的透明度變化梯度信息 //startOpacity、endOpacity 用來接收透明度變化過程的起始值與結束值 //timeRange 用來接收變化的持續時間範圍 //返回值表示指定的時間 time 是否在變化時間 timeRange 內 - (BOOL)getOpacityRampForTime:(CMTime)time startOpacity:(nullable float *)startOpacity endOpacity:(nullable float *)endOpacity timeRange:(nullable CMTimeRange *)timeRange; //獲取包含指定時間的裁剪區域的變化梯度信息 //startCropRectangle、endCropRectangle 用來接收變化過程的起始值與結束值 //timeRange 用來接收變化的持續時間範圍 //返回值表示指定的時間 time 是否在變化時間 timeRange 內 - (BOOL)getCropRectangleRampForTime:(CMTime)time startCropRectangle:(nullable CGRect *)startCropRectangle endCropRectangle:(nullable CGRect *)endCropRectangle timeRange:(nullable CMTimeRange *)timeRange NS_AVAILABLE(10_9, 7_0);

AVMutableVideoCompositionLayerInstruction

AVMutableVideoCompositionLayerInstruction 是 AVVideoCompositionLayerInstruction 的子類,它能夠改變 composition 中的 track 資源播放時的仿射變化、裁剪區域、透明度等信息。

相比於父類,該子類還提供了建立實例的方法:

//這兩個方法的區別在於,前者返回的實例對象的屬性 trackID 的值是 track 的 trackID 值 //而第二個方法的返回的實例對象的屬性 trackID 的值爲 kCMPersistentTrackID_Invalid + (instancetype)videoCompositionLayerInstructionWithAssetTrack:(AVAssetTrack *)track; + (instancetype)videoCompositionLayerInstruction;

該類的屬性表示 instruction 所做用的 track 的 ID

@property (nonatomic, assign) CMPersistentTrackID trackID;

設置了 trackID 後,經過下面的方法,進行剃度信息的設置:

//設置視頻中幀的仿射變化信息
//指定了變化的時間範圍、起始值和結束值,其中座標系的原點爲左上角,向下向右爲正方向
- (void)setTransformRampFromStartTransform:(CGAffineTransform)startTransform toEndTransform:(CGAffineTransform)endTransform timeRange:(CMTimeRange)timeRange; //設置 instruction 的 timeRange 範圍內指定時間的仿射變換,該值會一直保持,直到被再次設置 - (void)setTransform:(CGAffineTransform)transform atTime:(CMTime)time; //設置透明度的梯度信息,提供的透明度初始值和結束值應在0~1之間 //變化的過程是線形的 - (void)setOpacityRampFromStartOpacity:(float)startOpacity toEndOpacity:(float)endOpacity timeRange:(CMTimeRange)timeRange; //設置指定時間的透明度,該透明度會一直持續到下一個值被設置 - (void)setOpacity:(float)opacity atTime:(CMTime)time; //設置裁剪矩形的變化信息 - (void)setCropRectangleRampFromStartCropRectangle:(CGRect)startCropRectangle toEndCropRectangle:(CGRect)endCropRectangle timeRange:(CMTimeRange)timeRange NS_AVAILABLE(10_9, 7_0); //設置指定時間的裁剪矩形 - (void)setCropRectangle:(CGRect)cropRectangle atTime:(CMTime)time NS_AVAILABLE(10_9, 7_0);

AVVideoCompositionCoreAnimationTool

在自定義視頻播放時,可能須要添加水印、標題或者其餘的動畫效果,須要使用該類。該類一般用來協調離線視頻中圖層與動畫圖層的組合(如使用 AVAssetExportSession 和 AVAssetReader 、AVAssetReader 類導出視頻文件或讀取視頻文件時),而如果在線實時的視頻播放,應使用 AVSynchronizedLayer 類來同步視頻的播放與動畫的效果。

在使用該類時,注意動畫在整個視頻的時間線上都可以被修改,因此,動畫的開始時間應該設置爲 AVCoreAnimationBeginTimeAtZero ,這個值其實比 0 大,屬性值 removedOnCompletion 應該置爲 NO,以防當動畫執行結束後被移除,而且不該使用與任何的 UIView 相關聯的圖層。

做爲視頻組合的後期處理工具類,主要方法以下:

//向視頻組合中添加一個動畫圖層,這個圖層不能在任何圖層樹中 //提供的參數 trackID 應由方法 [AVAsset unusedTrackID] 獲得,它不與任何視頻資源的 trackID 相關 //AVVideoCompositionInstruction 的屬性 layerInstructions 包含的 AVVideoCompositionLayerInstruction 實例對象中應該有 //該 trackID 一致的 AVVideoCompositionLayerInstruction 實例對象,而且爲性能考慮,不該使用該對象設置 transform 的變化 //在 iOS 中,CALayer 做爲 UIView 的背景圖層,其內容的是否可以翻轉,由方法 contentsAreFlipped 決定(若是全部的圖層包括子圖層,該方法返回的值爲 YES 的個數爲奇數個,表示能夠圖層中內容能夠垂直翻轉) //因此這裏的 layer 若用來設置 UIView 的 layer 屬性,或做爲其中的子圖層,其屬性值 geometryFlipped 應設置爲 YES ,這樣則可以保持是否可以翻轉的結果一致 + (instancetype)videoCompositionCoreAnimationToolWithAdditionalLayer:(CALayer *)layer asTrackID:(CMPersistentTrackID)trackID; //將放在圖層 videoLayer 中的組合視頻幀同動畫圖層 animationLayer 中的內容一塊兒進行渲染,獲得最終的視頻幀 //一般,videoLayer 是 animationLayer 的子圖層,而 animationLayer 則不在任何圖層樹中 + (instancetype)videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:(CALayer *)videoLayer inLayer:(CALayer *)animationLayer; //複製 videoLayers 中的每個圖層,與 animationLayer一塊兒渲染獲得最中的幀 ////一般,videoLayers 中的圖層都在 animationLayer 的圖層樹中,而 animationLayer 則不屬於任何圖層樹 + (instancetype)videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayers:(NSArray<CALayer *> *)videoLayers inLayer:(CALayer *)animationLayer NS_AVAILABLE(10_9, 7_0);

AVVideoCompositionValidationHandling

當咱們通過編輯後獲得一個視頻資源 asset ,而且爲該資源設置了自定義播放信息 video composition ,須要驗證對於這個 asset 而言,video composition 是否有效,能夠調用 AVVideoComposition 的校驗方法。

/*
@param asset 
設置第一個參數的校驗內容,設置 nil 忽略這些校驗
1. 該方法能夠校驗 AVVideoComposition 的屬性 instructions 是否符合要求 2. 校驗 instructions 中的每一個 AVVideoCompositionInstruction 對象的 layerInstructions 屬性中的 每個 AVVideoCompositionLayerInstruction 對象 trackID 值是否對應 asset 中 track 的 ID 或 AVVideoComposition 的 animationTool 實例 3. 校驗時間 asset 的時長是否與 instructions 中的時間範圍相悖 @param timeRange 設置第二個參數的校驗內容 1. 校驗 instructions 的全部的時間範圍是否在提供的 timeRange 的範圍內, 若要忽略該校驗,能夠傳參數 CMTimeRangeMake(kCMTimeZero, kCMTimePositiveInfinity) @param validationDelegate 設置遵循 AVVideoCompositionValidationHandling 協議的代理類,用來處理校驗過程當中的報錯,能夠爲 nil */ - (BOOL)isValidForAsset:(nullable AVAsset *)asset timeRange:(CMTimeRange)timeRange validationDelegate:(nullable id<AVVideoCompositionValidationHandling>)validationDelegate NS_AVAILABLE(10_8, 5_0);

設置的代理對象要遵循協議 AVVideoCompositionValidationHandling ,該對象在實現下面的協議方法時,若修改了傳遞的 composition 參數,上面的校驗方法則會拋出異常。

該協議提供瞭如下回調方法,全部方法的返回值用來肯定是否繼續進行校驗以獲取更多的錯誤。

//報告 videoComposition 中有無效的值 - (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingInvalidValueForKey:(NSString *)key NS_AVAILABLE(10_8, 5_0); //報告 videoComposition 中有時間段沒有相對應的 instruction - (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingEmptyTimeRange:(CMTimeRange)timeRange NS_AVAILABLE(10_8, 5_0); //報告 videoComposition 中的 instructions 中 timeRange 無效的實例對象 //多是 timeRange 自己爲 CMTIMERANGE_IS_INVALID //或者是該時間段同上一個的 instruction 的 timeRange 重疊 //也多是其開始時間比上一個的 instruction 的 timeRange 的開始時間要早 - (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingInvalidTimeRangeInInstruction:(id<AVVideoCompositionInstruction>)videoCompositionInstruction NS_AVAILABLE(10_8, 5_0); //報告 videoComposition 中的 layer instruction 同調用校驗方法時指定的 asset 中 track 的 trackID 不一致 //也不與 composition 使用的 animationTool 的trackID 一致 - (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingInvalidTrackIDInInstruction:(id<AVVideoCompositionInstruction>)videoCompositionInstruction layerInstruction:(AVVideoCompositionLayerInstruction *)layerInstruction asset:(AVAsset *)asset NS_AVAILABLE(10_8, 5_0);

例程

下面的例子給出了將兩個視頻資源和一個音頻資源編輯組合爲一個資源文件的步驟。

  1. 要組合多個視聽資源,須要先建立一個 AVMutableComposition 實例對象,用來組合資源。
  2. 而後向 composition 中添加 AVMutableCompositionTrack 實例對象,爲性能考慮,對於非同時播放且相同類型的資源,應使用一個 AVMutableCompositionTrack 實例對象,因此這裏添加一個視頻類型的 composition track 和一個音頻類型的 composition track 便可。

    AVMutableComposition *mutableComposition = [AVMutableComposition composition]; AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; AVMutableCompositionTrack *audioCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
  3. 得到了擁有 composition track 的 composition 後,下一步就是將具體的視聽資源添加到組合中。

    AVURLAsset *firstVideoAsset = [AVURLAsset URLAssetWithURL:firstVideoUrl options:nil]; AVURLAsset *secondVideoAsset = [AVURLAsset URLAssetWithURL:secondVideoUrl options:nil]; AVURLAsset *audioAsset = [AVURLAsset URLAssetWithURL:audioUrl options:nil]; //獲取視聽資源中的第一個 asset track AVAssetTrack *firstVideoAssetTrack = [[firstVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVAssetTrack *secondVideoAssetTrack = [[secondVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; //第一個視頻插入的時間點是 kCMTimeZero [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, firstVideoAssetTrack.timeRange.duration) ofTrack:firstVideoAssetTrack atTime:kCMTimeZero error:nil]; //第二個視頻插入的時間點是第一個視頻結束的時間 [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, secondVideoAssetTrack.timeRange.duration) ofTrack:secondVideoAssetTrack atTime:firstVideoAssetTrack.timeRange.duration error:nil]; //音頻的持續時間是兩個視頻時間的總和 CMTime videoTotalDuration = CMTimeAdd(firstVideoAssetTrack.timeRange.duration, secondVideoAssetTrack.timeRange.duration); [audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoTotalDuration) ofTrack: atTime:kCMTimeZero error:nil];
  4. 檢查視頻的鏡頭是不是橫向模式,組合時,video track 老是被認爲是橫向模式,若是待組合的 video track 是縱向模式,那麼最終的視頻顯示將不符合預想,並且沒法將橫向模式和縱向模式的的視頻組合到一塊兒。

    //判斷第一個視頻的模式 BOOL isFirstVideoPortrait = NO; CGAffineTransform firstTransform = firstVideoAssetTrack.preferredTransform; if (firstTransform.a == 0 && firstTransform.d == 0 && (firstTransform.b == 1.0 || firstTransform.b == -1.0) && (firstTransform.c == 1.0 || firstTransform.c == -1.0)) { isFirstVideoPortrait = YES; } //判斷第二個視頻的模式 BOOL isSecondVideoPortrait = NO; CGAffineTransform secondTransform = secondVideoAssetTrack.preferredTransform; if (secondTransform.a == 0 && secondTransform.d == 0 && (secondTransform.b == 1.0 || secondTransform.b == -1.0) && (secondTransform.c == 1.0 || secondTransform.c == -1.0)) { isSecondVideoPortrait = YES; } //判斷兩個視頻的模式是否一致 if ((isFirstVideoAssetPortrait && !isSecondVideoAssetPortrait) || (!isFirstVideoAssetPortrait && isSecondVideoAssetPortrait)) { UIAlertView *incompatibleVideoOrientationAlert = [[UIAlertView alloc] initWithTitle:@"Error!" message:@"Cannot combine a video shot in portrait mode with a video shot in landscape mode." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil]; [incompatibleVideoOrientationAlert show]; return; }
  5. 當每一個視頻的方向是兼容的,那麼能夠對每一個視頻的圖層進行必要的調整。

    AVMutableVideoCompositionInstruction *firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, firstVideoAssetTrack.timeRange.duration); AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration, secondVideoAssetTrack.timeRange.duration); AVMutableVideoCompositionLayerInstruction *firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack]; [firstVideoLayerInstruction setTransform:firstTransform atTime:kCMTimeZero]; AVMutableVideoCompositionLayerInstruction *secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack]; [secondVideoLayerInstruction setTransform:secondTransform atTime:firstVideoAssetTrack.timeRange.duration]; firstVideoCompositionInstruction.layerInstructions = @[firstVideoLayerInstruction]; secondVideoCompositionInstruction.layerInstructions = @[secondVideoLayerInstruction]; AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition]; mutableVideoComposition.instructions = @[firstVideoCompositionInstruction, secondVideoCompositionInstruction];
  6. 檢查視頻方向的兼容性以後,須要調整視頻組合渲染區域的大小,設置視頻幀的刷新頻率,以兼容每個視頻的播放。

    //獲取視頻的原播放區域
    CGSize naturalSizeFirst, naturalSizeSecond; if (isFirstVideoAssetPortrait) { naturalSizeFirst = CGSizeMake(firstVideoAssetTrack.naturalSize.height, firstVideoAssetTrack.naturalSize.width); naturalSizeSecond = CGSizeMake(secondVideoAssetTrack.naturalSize.height, secondVideoAssetTrack.naturalSize.width); } else { naturalSizeFirst = firstVideoAssetTrack.naturalSize; naturalSizeSecond = secondVideoAssetTrack.naturalSize; } //設置的渲染區域要能包含兩個視頻的播放區域 float renderWidth, renderHeight; if (naturalSizeFirst.width > naturalSizeSecond.width) { renderWidth = naturalSizeFirst.width; } else { renderWidth = naturalSizeSecond.width; } if (naturalSizeFirst.height > naturalSizeSecond.height) { renderHeight = naturalSizeFirst.height; } else { renderHeight = naturalSizeSecond.height; } mutableVideoComposition.renderSize = CGSizeMake(renderWidth, renderHeight); //設置幀每一秒刷新30次 mutableVideoComposition.frameDuration = CMTimeMake(1,30);
  7. 最後將組合的視聽資源導出到一個單獨的文件中並保存到資源庫。

    static NSDateFormatter *kDateFormatter; if (!kDateFormatter) { kDateFormatter = [[NSDateFormatter alloc] init]; kDateFormatter.dateStyle = NSDateFormatterMediumStyle; kDateFormatter.timeStyle = NSDateFormatterShortStyle; } AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality]; exporter.outputURL = [[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:@YES error:nil] URLByAppendingPathComponent:[kDateFormatter stringFromDate:[NSDate date]]] URLByAppendingPathExtension:CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie, kUTTagClassFilenameExtension))]; exporter.outputFileType = AVFileTypeQuickTimeMovie; exporter.shouldOptimizeForNetworkUse = YES; exporter.videoComposition = mutableVideoComposition; //異步導出 [exporter exportAsynchronouslyWithCompletionHandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ if (exporter.status == AVAssetExportSessionStatusCompleted) { //保存文件到媒體庫 ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init]; if ([assetsLibrary videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) { [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL completionBlock:NULL]; } } }); }];

媒體資源捕獲

經過麥克風、攝像機等設備,能夠捕獲外界的聲音和影像。要處理設備捕獲的數據,須要使用 AVCaptureDevice 類描述設備,使用 AVCaptureInput 配置數據從設備的輸入,使用 AVCaptureOutput 類管理數據到文件的寫入,而數據的輸入到寫出,須要使用 AVCaptureSession 類進行協調。此外,可使用 AVCaptureVideoPreviewLayer 類顯示相機正在拍攝的畫面。

一個設備能夠有多個輸入,使用 AVCaptureInputPort 類描述這些輸入,用 AVCaptureConnection 類描述具體類型的輸入與輸出的關係,能夠實現更精細的數據處理。

AVCaptureSession

AVCaptureSession 是捕獲視聽數據的核心類,它協調數據的輸入和輸出。建立一個 AVCaptureSession 類的對象時,能夠指定最終獲得的視聽數據的質量,固然這個質量與設備也有關係,一般在設置以前,能夠調用方法判斷 session 是否支持要設置的質量。

AVCaptureSession 類實例可設置的數據質量有 AVCaptureSessionPresetHigh 、AVCaptureSessionPresetMedium 、AVCaptureSessionPresetLow 、AVCaptureSessionPreset320x240 等。在進行設置以前,能夠調用 AVCaptureSession 中的方法進行校驗。

- (BOOL)canSetSessionPreset:(NSString*)preset;

設置好對象後,可調用下面的方法,添加、移除輸入、輸出。

- (BOOL)canAddInput:(AVCaptureInput *)input; - (void)addInput:(AVCaptureInput *)input; - (void)removeInput:(AVCaptureInput *)input; - (BOOL)canAddOutput:(AVCaptureOutput *)output; - (void)addOutput:(AVCaptureOutput *)output; - (void)removeOutput:(AVCaptureOutput *)output; - (void)addInputWithNoConnections:(AVCaptureInput *)input NS_AVAILABLE(10_7, 8_0); - (void)addOutputWithNoConnections:(AVCaptureOutput *)output NS_AVAILABLE(10_7, 8_0); - (BOOL)canAddConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 8_0); - (void)addConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 8_0); - (void)removeConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 8_0);

開始執行 session 或者結束執行,調用下面的方法。

- (void)startRunning; - (void)stopRunning;

對於正在執行中的 session ,要對其進行改變,所做出的改變,應放在下面兩個方法之間。

- (void)beginConfiguration; - (void)commitConfiguration;

AVCaptureSession 開始執行、結束執行、執行過程當中出錯或被打斷時,都會發出通知,經過註冊下面的通知,能夠獲取咱們感興趣的信息。

  • AVCaptureSessionRuntimeErrorNotification 經過 AVCaptureSessionErrorKey 能夠獲取出錯的緣由
  • AVCaptureSessionDidStartRunningNotification 開始 session
  • AVCaptureSessionDidStopRunningNotification 結束 session
  • AVCaptureSessionWasInterruptedNotification 經過 AVCaptureSessionInterruptionReasonKey 能夠獲取被打斷的緣由
  • AVCaptureSessionInterruptionEndedNotification 打斷結束,session 從新開始

AVCaptureDevice

AVCaptureDevice 是用來描述設備屬性的類,要捕獲視聽數據,須要獲取相應的設備,使用該類獲取有效的設備資源。這個設備資源列表是隨時變更的,其在變更時,會發送 AVCaptureDeviceWasConnectedNotification 或 AVCaptureDeviceWasDisconnectedNotification 通知,以告知有設備鏈接或斷開。

在獲取設備以前,要先肯定要獲取的設備的類型 AVCaptureDeviceType ,設備的位置 AVCaptureDevicePosition ,也能夠經過要獲取的媒體數據類型進行設備的選擇。

獲取設備後,能夠保存它的惟一標識、模型標識、名稱等信息,以待下次用來獲取設備。

+ (NSArray *)devices; + (NSArray *)devicesWithMediaType:(NSString *)mediaType; + (AVCaptureDevice *)defaultDeviceWithMediaType:(NSString *)mediaType; + (AVCaptureDevice *)deviceWithUniqueID:(NSString *)deviceUniqueID; @property(nonatomic, readonly) NSString *uniqueID; @property(nonatomic, readonly) NSString *modelID; @property(nonatomic, readonly) NSString *localizedName; //校驗得到的設備可否提供相應的媒體數據類型 - (BOOL)hasMediaType:(NSString *)mediaType; //校驗得到的設備可否支持相應的配置 - (BOOL)supportsAVCaptureSessionPreset:(NSString *)preset;

獲取一個設備後,能夠經過修改它的屬性來知足本身的須要。

  • flashMode 閃光燈的模式(AVCaptureFlashModeOff 、AVCaptureFlashModeOn 、AVCaptureFlashModeAuto)
  • torchMode 手電筒的模式(AVCaptureTorchModeOff 、AVCaptureTorchModeOn 、AVCaptureTorchModeAuto)
  • torchLevel 手電筒的亮度(0~1)
  • focusMode 聚焦模式(AVCaptureFocusModeLocked 、AVCaptureFocusModeAutoFocus 、AVCaptureFocusModeContinuousAutoFocus)
  • exposureMode 曝光模式(AVCaptureExposureModeLocked 、AVCaptureExposureModeAutoExpose 、AVCaptureExposureModeContinuousAutoExposure 、AVCaptureExposureModeCustom)
  • whiteBalanceMode 白平衡模式(AVCaptureWhiteBalanceModeLocked 、AVCaptureWhiteBalanceModeAutoWhiteBalance 、AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance)

在修改這些屬性時,應先判斷當前設備是否支持要設置的屬性值,而且全部的屬性修改都要放在下面兩個方法之間,以保證屬性可以被正確設置。

- (BOOL)lockForConfiguration:(NSError **)outError; - (void)unlockForConfiguration;

在調用硬件設備以前,應先判斷應用是否擁有相應的權限,其權限分爲如下幾種:

  • AVAuthorizationStatusNotDetermined 未定義
  • AVAuthorizationStatusRestricted 無權限(因某些緣由,系統拒絕權限)
  • AVAuthorizationStatusDenied 無權限(用戶拒絕)
  • AVAuthorizationStatusAuthorized 有權限
//校驗權限 + (AVAuthorizationStatus)authorizationStatusForMediaType:(NSString *)mediaType NS_AVAILABLE_IOS(7_0); //請求權限,handler 處理會在任意線程中執行,因此須要在主線程中執行的處理由用戶負責指定 + (void)requestAccessForMediaType:(NSString *)mediaType completionHandler:(void (^)(BOOL granted))handler NS_AVAILABLE_IOS(7_0);

AVCaptureDeviceInput

AVCaptureDeviceInput 是 AVCaptureInput 的子類,使用一個 AVCaptureDevice 類實例建立該類的實例,其管理設備的輸入。

在建立了實例對象後,將其添加到 session 中。

NSError *error; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (input && [session canAddInput:input]) { [captureSession addInput:captureDeviceInput]; }

AVCaptureOutput

AVCaptureOutput 是一個抽象類,一般使用的是它的子類。

  • AVCaptureMovieFileOutput 用來生成一個影視文件
  • AVCaptureVideoDataOutput 用來處理輸入的視頻的幀
  • AVCaptureAudioDataOutput 用來處理音頻數據
  • AVCaptureStillImageOutput 用來獲取圖片

在建立了具體的子類後,將它添加到 session 中。

AVCaptureMovieFileOutput *movieOutput = [[AVCaptureMovieFileOutput alloc] init]; if ([session canAddOutput:movieOutput]) { [session addOutput:movieOutput]; }

AVCaptureFileOutput

AVCaptureFileOutput 是 AVCaptureOutput 的子類,是 AVCaptureMovieFileOutput 、AVCaptureAudioFileOutput 的父類。這個類中定義了文件輸出時的地址、時長、容量等屬性。

//當前記錄的數據的文件的地址 @property(nonatomic, readonly) NSURL *outputFileURL; //開始文件的記錄,指定文件的地址,以及記錄過程當中或結束時要通知的代理對象 //指定的 outputFileURL 必需是有效的且沒有文件佔用 - (void)startRecordingToOutputFileURL:(NSURL*)outputFileURL recordingDelegate:(id<AVCaptureFileOutputRecordingDelegate>)delegate; //該方法能夠中止數據向文件中寫入 //若是要中止一個文件的寫入轉而指定另外一個文件的寫入,不該調用該方法,只需直接調用上面的方法 //當因該方法的調用、出錯、或寫入文件的變動致使當前文件開始中止寫入時,最後傳入的緩存數據仍會在後臺被寫入 //不管什麼時候,要使用文件,都須要等指定的代理對象被告知文件的寫入已經結束以後進行 - (void)stopRecording; //判斷當前是否有數據被寫入文件 @property(nonatomic, readonly, getter=isRecording) BOOL recording; //表示到目前爲止,當前文件已經記錄了多長時間 @property(nonatomic, readonly) CMTime recordedDuration; //表示到目前爲止,當前文件已經記錄了多少個字節 @property(nonatomic, readonly) int64_t recordedFileSize; /** 下面三個值對文件的記錄進行了限制,若果達到限制,則會在回調方法 captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: 中傳遞相應的錯誤 */ //表示當前文件可以記錄的最長時間,kCMTimeInvalid 表示無時間限制 @property(nonatomic) CMTime maxRecordedDuration; //表示當前文件可以記錄的最大字節數,0 表示無大小限制 @property(nonatomic) int64_t maxRecordedFileSize; //表示記錄當前文件時須要保留的最小字節數 @property(nonatomic) int64_t minFreeDiskSpaceLimit; //在 Mac OS X 系統下,經過指定遵循 AVCaptureFileOutputDelegate 協議的代理對象,來實現緩存數據的精確記錄 @property(nonatomic, assign) id<AVCaptureFileOutputDelegate> delegate NS_AVAILABLE(10_7, NA); /** 在 Mac OS X 系統下,這個屬性和方法能夠判斷記錄是否中止,以及控制數據向文件中的中止寫入和從新開始寫入 */ @property(nonatomic, readonly, getter=isRecordingPaused) BOOL recordingPaused NS_AVAILABLE(10_7, NA); - (void)pauseRecording NS_AVAILABLE(10_7, NA); - (void)resumeRecording NS_AVAILABLE(10_7, NA);

AVCaptureFileOutputRecordingDelegate

AVCaptureFileOutputRecordingDelegate 是文件記錄過程當中須要用到的協議,它一般的做用是告知代理對象文件記錄結束了。

//這個代理方法是遵循該協議的代理對象必需要實現的方法
//每個文件記錄請求,最終都會調用這個方法,即便沒有數據成功寫入文件
//當 error 返回時,文件也可能成功保存了,應檢查 error 中的 AVErrorRecordingSuccessfullyFinishedKey 信息,查看具體錯誤 - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error; //當數據寫入文件後調用,若是數據寫入失敗,該方法可能不會被調用 - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections; /** 在 Mac OS X 系統下,當文件的記錄被暫停或從新開始,會調用下面的方法,若是記錄被終止,不會調用下面的方法 */ - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didPauseRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections NS_AVAILABLE(10_7, NA); - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didResumeRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections NS_AVAILABLE(10_7, NA); //在 Mac OS X 系統下,當記錄將被中止,不管是主動的仍是被動的,都會調用下面的方法 - (void)captureOutput:(AVCaptureFileOutput *)captureOutput willFinishRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections error:(NSError *)error NS_AVAILABLE(10_7, NA);

AVCaptureFileOutputDelegate

AVCaptureFileOutputDelegate 這個協議只用於 Mac OS X 系統下,它給了客戶端精準操控數據的機會。

/** 在 Mac OS X 10.8 系統以前,實現代理方法 captureOutput:didOutputSampleBuffer:fromConnection: 後即可以在該方法中實現數據記錄的準確開始或結束,而要實如今任一一個畫面幀處開始或中止數據的記錄,要對每收到的 幀數據進行預先處理,這個過程消耗電能、產生熱量、佔用 CPU 資源,因此在 Mac OS X 10.8 及其以後的系統,提供了 下面的代理方法,來肯定客戶端需不須要隨時進行記錄的開始或中止。 若是這個方法返回 NO ,對數據記錄的設置將在開啓記錄以後進行。 */ - (BOOL)captureOutputShouldProvideSampleAccurateRecordingStart:(AVCaptureOutput *)captureOutput NS_AVAILABLE(10_8, NA); /** 若是上面的方法返回了 YES ,那麼客戶端即可以使用下面的方法對每個視頻幀數據或音頻數據進行操做 爲了提升性能,緩存池中的緩存變量的內存一般會被複用,若是長時間使用緩存變量,那麼新的緩存數據沒法複製到 相應的內存中便會被廢棄,因此若須要長時間使用緩存數據 sampleBuffer ,應複製一份,使其自己可以被系統複用 */ - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, NA);

AVCaptureMovieFileOutput

AVCaptureMovieFileOutput 是 AVCaptureFileOutput 的子類,它實現了在 AVCaptureFileOutput 中聲明的視頻數據記錄方法,而且能夠設置數據的格式、寫入元數據、編碼方式等屬性。

//若是視頻數據按片斷寫入,該值指定片斷的時長,默認值是 10 秒 //該值爲 kCMTimeInvalid 表示不使用片斷對視頻進行記錄,這樣視頻只能一次寫入,不能被打斷 //改變該值不影響當前正在寫入的片斷時長 @property(nonatomic) CMTime movieFragmentInterval; //向文件中添加的 AVMetadataItem 類元數據 @property(nonatomic, copy) NSArray *metadata; //獲取記錄 connection 中數據時,使用的設置 - (NSDictionary *)outputSettingsForConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 10_0); //設置記錄 connection 中數據時,使用的設置 AVVideoSettings.h //outputSettings 爲空,表示在將 connection 中的數據寫入文件以前,其格式不作改變 //outputSettings 爲 nil 時,其數據格式將由 session preset 決定 - (void)setOutputSettings:(NSDictionary *)outputSettings forConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 10_0); //在 iOS 系統下,獲取有效的編碼格式,做爲 AVVideoCodecKey 的值,使用上面的方法進行設置 @property(nonatomic, readonly) NSArray *availableVideoCodecTypes NS_AVAILABLE_IOS(10_0); //設置文件記錄過程當中,是否建立一個元數據對 connection 的 videoOrientation 和 videoMirrored 屬性變化進行跟蹤記錄 //connection 的屬性 mediaType 的值必需是 AVMediaTypeVideo //該值的設置只在記錄開始以前有效,開始記錄以後改變該值無效果 - (void)setRecordsVideoOrientationAndMirroringChanges:(BOOL)doRecordChanges asMetadataTrackForConnection:(AVCaptureConnection *)connection NS_AVAILABLE_IOS(9_0); //判斷該類實例對象是否會在記錄的過程當中建立一個 timed metadata track 記錄 connection 的 videoOrientation 和 videoMirrored 屬性變化狀況 - (BOOL)recordsVideoOrientationAndMirroringChangesAsMetadataTrackForConnection:(AVCaptureConnection *)connection NS_AVAILABLE_IOS(9_0);

AVCaptureAudioFileOutput

AVCaptureAudioFileOutput 是 AVCaptureFileOutput 的子類,該類用於將媒體數據記錄爲一個音頻文件。

//返回該類支持的音頻文件類型 + (NSArray *)availableOutputFileTypes; //開始記錄音頻文件 - (void)startRecordingToOutputFileURL:(NSURL*)outputFileURL outputFileType:(NSString *)fileType recordingDelegate:(id<AVCaptureFileOutputRecordingDelegate>)delegate; //要寫入音頻文件中的元數據 AVMetadataItem 集合 @property(nonatomic, copy) NSArray *metadata; //寫入的音頻文件的設置 AVAudioSettings.h @property(nonatomic, copy) NSDictionary *audioSettings;

AVCaptureVideoDataOutput

AVCaptureVideoDataOutput 是 AVCaptureOutput 的子類,該類能夠用來處理捕獲的每個視頻幀數據。建立一個該類的實例對象後,要調用下面的方法設置一個代理對象,及調用代理對象所實現的協議方法的隊列。

- (void)setSampleBufferDelegate:(id<AVCaptureVideoDataOutputSampleBufferDelegate>)sampleBufferDelegate queue:(dispatch_queue_t)sampleBufferCallbackQueue;

指定的隊列 sampleBufferCallbackQueue 必需是串行隊列以保證傳遞的幀是按記錄時間前後傳遞的。

//設置輸出的視頻要進行怎樣的格式處理 //設置爲空([NSDictionary dictionary])表示不改變輸入時的視頻格式 //設置爲 nil 表示未壓縮格式 @property(nonatomic, copy) NSDictionary *videoSettings; //獲取 kCVPixelBufferPixelFormatTypeKey 的有效值 @property(nonatomic, readonly) NSArray *availableVideoCVPixelFormatTypes NS_AVAILABLE(10_7, 5_0); //獲取 AVVideoCodecKey 的有效值 @property(nonatomic, readonly) NSArray *availableVideoCodecTypes NS_AVAILABLE(10_7, 5_0); //表示當回調隊列阻塞時,是否馬上丟棄新接收的幀數據 @property(nonatomic) BOOL alwaysDiscardsLateVideoFrames;

AVCaptureVideoDataOutputSampleBufferDelegate

該協議用來處理接收的每個幀數據,或者提示客戶端有幀數據被丟棄。

//接收到一個幀數據時,在指定的串行隊列中調用該方法,攜帶幀數據幷包含有其餘幀信息
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection; //丟棄一個幀數據時,在指定的串行隊列中調用該方法,sampleBuffer 只攜帶幀信息,具體幀數據並未攜帶 - (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 6_0);

AVCaptureVideoPreviewLayer

AVCaptureVideoPreviewLayer 是 CALayer 的子類,使用該類能夠實現捕獲視頻的顯示。使用一個 session 建立一個該類對象,然後將該類對象插入到圖層樹中,從而顯示捕獲的視頻。

//建立方法 + (instancetype)layerWithSession:(AVCaptureSession *)session; - (instancetype)initWithSession:(AVCaptureSession *)session;

修改 AVCaptureVideoPreviewLayer 的屬性 videoGravity 值,能夠選擇顯示捕獲視頻時的界面大小變化方式,它有如下可選值:

  • AVLayerVideoGravityResize 默認值,直接鋪滿屏幕,及時畫面變形
  • AVLayerVideoGravityResizeAspect 保持畫面的橫縱比,不鋪滿屏幕,多餘的空間顯示黑色
  • AVLayerVideoGravityResizeAspectFill 保持畫面的橫縱比,鋪滿屏幕,多餘的畫面進行裁剪

AVCaptureAudioDataOutput

AVCaptureAudioDataOutput 是 AVCaptureOutput 的子類,該類能夠處理接收到的音頻數據。同 AVCaptureVideoDataOutput 相似,該類也提供了一個方法,用於設置代理對象,以及調用代理對象實現的協議方法時的隊列。

- (void)setSampleBufferDelegate:(id<AVCaptureAudioDataOutputSampleBufferDelegate>)sampleBufferDelegate queue:(dispatch_queue_t)sampleBufferCallbackQueue;

AVCaptureAudioDataOutputSampleBufferDelegate

該協議提供了一個方法,用來實現對音頻數據的接收處理。

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;

對於每一個設備,其支持播放或捕獲的媒體資源都不相同,經過 AVCaptureDeviceFormat 類能夠獲取相關信息。

視聽資源讀寫

對媒體數據資源進行簡單的轉碼或裁剪,使用 AVAssetExportSession 類便足夠了,可是更深層次的修改媒體資源,便須要用到 AVAssetReader 類和 AVAssetWriter 類。

AVAssetReader 只能與一個資源 asset 相關聯,且不能用來讀取實時數據,在開始讀取數據以前,須要爲 reader 添加 AVAssetReaderOutput 的實例對象。這個實例對象描述的是待讀取的數據資源來源類型,一般使用 AVAssetReaderAudioMixOutput 、AVAssetReaderTrackOutput 、AVAssetReaderVideoCompositionOutput 三種子類。

AVAssetWriter 能夠未來自多個數據源的數據以指定的格式寫入到一個指定的文件中,且其只能對應一個文件。在寫文件以前,須要用每個 AVAssetWriterInput 類實例對象來描述相應的數據源。每個 AVAssetWriterInput 實例對象接收的數據都應是 CMSampleBufferRef 類型的變量。若是使用 AVAssetWriterInputPixelBufferAdaptor 類也能夠直接將 CVPixelBufferRef 類型的變量數據添加到 writer input 中。

AVAssetReader 與 AVAssetWriter 結合起來使用,即可以對讀取的數據進行相應的編輯修改,然後寫入到一個文件中並保存。

AVAssetReader

使用該類讀取媒體資源,其提供的初始化方法與一個 asset 相關聯。

//對於提供的參數 asset ,若是是可被修改的,那麼在開始讀取操做後,對其進行了修改,以後的讀取操做都是無效的 + (nullable instancetype)assetReaderWithAsset:(AVAsset *)asset error:(NSError * _Nullable * _Nullable)outError; - (nullable instancetype)initWithAsset:(AVAsset *)asset error:(NSError * _Nullable * _Nullable)outError NS_DESIGNATED_INITIALIZER; //當前讀取操做的狀態,可取值有 AVAssetReaderStatusUnknown 、AVAssetReaderStatusReading 、 AVAssetReaderStatusCompleted 、AVAssetReaderStatusFailed 、AVAssetReaderStatusCancelled @property (readonly) AVAssetReaderStatus status; //當 status 的值爲 AVAssetReaderStatusFailed 時,描述錯誤信息 @property (readonly, nullable) NSError *error; //限制可讀取的資源的時間範圍 @property (nonatomic) CMTimeRange timeRange; //判斷可否添加該數據源 - (BOOL)canAddOutput:(AVAssetReaderOutput *)output; //添加數據源 - (void)addOutput:(AVAssetReaderOutput *)output; //開始讀取 - (BOOL)startReading; //結束讀取 - (void)cancelReading;

AVAssetReaderOutput

AVAssetReaderOutput 是用來描述待讀取的數據的抽象類,讀取資源時,應建立該類的對象,並添加到相應的 AVAssetReader 實例對象中去。

//獲取的媒體數據的類型 @property (nonatomic, readonly) NSString *mediaType; //是否拷貝緩存中的數據到客戶端,默認 YES ,客戶端能夠隨意修改數據,可是爲優化性能,一般設爲 NO @property (nonatomic) BOOL alwaysCopiesSampleData NS_AVAILABLE(10_8, 5_0); //同步獲取下一個緩存數據,使用返回的數據結束後,應使用 CFRelease 函數將其釋放 //當錯誤或沒有數據可讀取時,返回 NULL ,返回空後,應檢查相關聯的 reader 的狀態 - (nullable CMSampleBufferRef)copyNextSampleBuffer CF_RETURNS_RETAINED; //是否支持從新設置數據的讀取時間範圍,即可否修改 reader 的 timeRange 屬性 @property (nonatomic) BOOL supportsRandomAccess NS_AVAILABLE(10_10, 8_0); //設置從新讀取的時間範圍,這個時間範圍集合中的每個時間範圍的開始時間必需是增加的且各個時間範圍不能重疊 //應在 reader 調用 copyNextSampleBuffer 方法返回 NULL 以後纔可調用 - (void)resetForReadingTimeRanges:(NSArray<NSValue *> *)timeRanges NS_AVAILABLE(10_10, 8_0); //該方法調用後,上面的方法即不可再調用,同時 reader 的狀態也不會被阻止變爲 AVAssetReaderStatusCompleted 了 - (void)markConfigurationAsFinal NS_AVAILABLE(10_10, 8_0);

AVAssetReaderTrackOutput

AVAssetReaderTrackOutput 是 AVAssetReaderOutput 的子類,它用來描述待讀取的數據來自 asset track ,在讀取前,還能夠對數據的格式進行修改。

//初始化方法,參數中指定了 track 和 媒體的格式 //指定的 track 應在 reader 的 asset 中 + (instancetype)assetReaderTrackOutputWithTrack:(AVAssetTrack *)track outputSettings:(nullable NSDictionary<NSString *, id> *)outputSettings; - (instancetype)initWithTrack:(AVAssetTrack *)track outputSettings:(nullable NSDictionary<NSString *, id> *)outputSettings NS_DESIGNATED_INITIALIZER; //指定音頻處理時的算法 @property (nonatomic, copy) NSString *audioTimePitchAlgorithm NS_AVAILABLE(10_9, 7_0);

AVAssetReaderAudioMixOutput

AVAssetReaderAudioMixOutput 是 AVAssetReaderOutput 的子類,它用來描述待讀取的數據來自音頻組合數據。建立該類實例對象提供的參數 audioTracks 集合中的每個 asset track 都屬於相應的 reader 中的 asset 實例對象,且類型爲 AVMediaTypeAudio 。 
參數 audioSettings 給出了音頻數據的格式設置。

+ (instancetype)assetReaderAudioMixOutputWithAudioTracks:(NSArray<AVAssetTrack *> *)audioTracks audioSettings:(nullable NSDictionary<NSString *, id> *)audioSettings; - (instancetype)initWithAudioTracks:(NSArray<AVAssetTrack *> *)audioTracks audioSettings:(nullable NSDictionary<NSString *, id> *)audioSettings NS_DESIGNATED_INITIALIZER;

此外,該類的 audioMix 屬性,描述了從多個 track 中讀取的音頻的音量變化狀況。

@property (nonatomic, copy, nullable) AVAudioMix *audioMix;

AVAssetReaderVideoCompositionOutput

AVAssetReaderVideoCompositionOutput 是 AVAssetReaderOutput 的子類,該類用來表示要讀取的類是組合的視頻數據。 
同 AVAssetReaderAudioMixOutput 相似,該類也提供了兩個建立實例的方法,須要提供的參數的 videoTracks 集合中每個 track 都是 
與 reader 相關聯的 asset 中的 track 。

+ (instancetype)assetReaderVideoCompositionOutputWithVideoTracks:(NSArray<AVAssetTrack *> *)videoTracks videoSettings:(nullable NSDictionary<NSString *, id> *)videoSettings; - (instancetype)initWithVideoTracks:(NSArray<AVAssetTrack *> *)videoTracks videoSettings:(nullable NSDictionary<NSString *, id> *)videoSettings NS_DESIGNATED_INITIALIZER;

該類的屬性 videoComposition 一樣描述了每一個 track 的幀的顯示方式。

@property (nonatomic, copy, nullable) AVVideoComposition *videoComposition;

使用 AVOutputSettingsAssistant 類能夠獲取簡單的編碼設置

 
轉自:https://blog.csdn.net/u011374318/article/details/78829096
相關文章
相關標籤/搜索