概述
AVFoundation 是一個(gè)可以用來使用和創(chuàng)建基于時(shí)間的視聽媒體數(shù)據(jù)的框架。AVFoundation 的構(gòu)建考慮到了目前的硬件環(huán)境和應(yīng)用程序,其設(shè)計(jì)過程高度依賴多線程機(jī)制。充分利用了多核硬件的優(yōu)勢并大量使用block和GCD機(jī)制,將復(fù)雜的計(jì)算機(jī)進(jìn)程放到了后臺線程運(yùn)行。會(huì)自動(dòng)提供硬件加速操作,確保在大部分設(shè)備上應(yīng)用程序能以最佳性能運(yùn)行。該框架就是針對64位處理器設(shè)計(jì)的,可以發(fā)揮64位處理器的所有優(yōu)勢。

AVAsset
AVFondation 是一個(gè)非常強(qiáng)大且可擴(kuò)展的框架,包括對媒體的捕捉、組合、播放和處理等廣泛功能,同時(shí)它還有別于傳統(tǒng)面向文件的音頻類,框架把所有的代碼設(shè)計(jì)圍繞著 “資源” 進(jìn)行。資源最重要的類是 AVAsset,它是 AVFoundation 設(shè)計(jì)的核心,在幾乎所有特性和功能的開發(fā)中扮演著至關(guān)重要的角色。AVAsset 是一個(gè)抽象類,定義了媒體資源混合呈現(xiàn)的方式,將媒體的靜態(tài)屬性模塊化成一個(gè)整體,比它們的標(biāo)題、時(shí)長、元數(shù)據(jù)。
AVAsset 不需要考慮媒體資源所具有的兩個(gè)重要范疇。第一是它提供了對基本媒體格式的層抽象。也就是說無論是處理影片、音頻,對開發(fā)者和框架而言面對的只有資源這個(gè)概念,讓開發(fā)者面對不同格式的內(nèi)容的時(shí)候有統(tǒng)一的處理方法,不用考慮許多編解碼的細(xì)節(jié)。第二是它隱藏了資源的位置信息。當(dāng)我們處理資源的時(shí)候,可以通過URL來創(chuàng)建資源,這個(gè)地址可能在本地、也可能在遠(yuǎn)程服務(wù)器上。
AVAssetTrack
AVAsset 本身不是媒體資源,但是它可以作為時(shí)基媒體的容器。它由一個(gè)或多個(gè)帶有描述自身元數(shù)據(jù)的媒體組成。我們使用 AVAssetTrack 類代表保存在資源中統(tǒng)一類型媒體,并對每個(gè)資源建立相應(yīng)的模型。AVAssetTrack 常見的形態(tài)是音頻和視頻流,但是它還可以表示文本、副標(biāo)題、隱藏字幕等媒體類型。

在 AVAsset 中,可以通過 TrackID,獲得特定的 AVAssetTrack。
- (nullable AVAssetTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;
除了通過trackID獲得track之外,AVAsset中還提供了其他3中方式獲得track
@property (nonatomic, readonly) NSArray<AVAssetTrack *> *tracks;
- (NSArray<AVAssetTrack *> *)tracksWithMediaType:(NSString *)mediaType;
- (NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;
tracks中包含了當(dāng)前 AVAsset 中的所有track,通過遍歷我們可以獲得想要的track。- (NSArray<AVAssetTrack *> *)tracksWithMediaType:(NSString *)mediaType; 方法會(huì)根據(jù)指定的媒體類型返回一個(gè)track數(shù)組,數(shù)組中包含著Asset中所有指定媒體類型的track。如果Asset中沒有這個(gè)媒體類型的track,返回一個(gè)空數(shù)組。AVMediaFormat 中有以下幾種媒體類型:
AVF_EXPORT NSString *const AVMediaTypeVideo NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMediaTypeAudio NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMediaTypeText NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMediaTypeClosedCaption NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMediaTypeSubtitle NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMediaTypeTimecode NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMediaTypeMetadata NS_AVAILABLE(10_8, 6_0);
AVF_EXPORT NSString *const AVMediaTypeMuxed NS_AVAILABLE(10_7, 4_0);
- (NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic; 方法會(huì)根據(jù)指定的媒體特征返回track數(shù)組。如果 AVAsset 中沒有這個(gè)媒體特征的track,返回空數(shù)組。AVMediaFormat中一共有以下幾種媒體特征:
NSString *const AVMediaTypeMetadataObject;
NSString *const AVMediaCharacteristicVisual;
NSString *const AVMediaCharacteristicAudible;
NSString *const AVMediaCharacteristicLegible;
NSString *const AVMediaCharacteristicFrameBased;
NSString *const AVMediaCharacteristicIsMainProgramContent;
NSString *const AVMediaCharacteristicIsAuxiliaryContent;
NSString *const AVMediaCharacteristicContainsOnlyForcedSubtitles;
NSString *const AVMediaCharacteristicTranscribesSpokenDialogForAccessibility;
NSString *const AVMediaCharacteristicDescribesMusicAndSoundForAccessibility;
NSString *const AVMediaCharacteristicEasyToRead;
NSString *const AVMediaCharacteristicDescribesVideoForAccessibility;
NSString *const AVMediaCharacteristicLanguageTranslation;
NSString *const AVMediaCharacteristicDubbedTranslation;
NSString *const AVMediaCharacteristicVoiceOverTranslation;
創(chuàng)建資源
AVAsset 是一個(gè)抽象類,不能直接實(shí)例化。當(dāng)使用 assetWithURL: 方法創(chuàng)建實(shí)例的時(shí)候,實(shí)際上創(chuàng)建的是它的子類,子類為AVURLAsset 。
NSURL *mp3URL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"]];
AVAsset *asset = [AVAsset assetWithURL:mp3URL];
我們也可以直接創(chuàng)建 AVURLAsset 實(shí)例,我們可以傳遞更多的參數(shù),來更精確地獲取計(jì)時(shí)相關(guān)的信息。當(dāng)然這樣做可能需要加載更長的時(shí)間,以便獲取更準(zhǔn)確的時(shí)長及時(shí)間信息。
NSDictionary *dict = @{
AVURLAssetPreferPreciseDurationAndTimingKey : @(YES)
};
NSURL *mp3URL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"]];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:mp3URL options:dict];
照片庫
用戶使用相機(jī)或者第三方視頻捕捉程序捕捉的視頻,它們通常被保存在用戶的照片庫中。我們可以通過 AssetsLibrary 來訪問照片,并創(chuàng)建 AVAsset 對象。
ALAssetsLibrary *assetLib = [[ALAssetsLibrary alloc] init];
[assetLib enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group setAssetsFilter:[ALAssetsFilter allVideos]];
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0]
options:0
usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
NSURL *url = [[result defaultRepresentation] url];
AVAsset *asset = [AVAsset assetWithURL:url];
}
}];
} failureBlock:^(NSError *error) {
NSLog(@"%@", [error localizedDescription]);
}];
異步載入
AVAsset 具有多種有用的方法和屬性,可以提供有關(guān)的資源信息,比如時(shí)長、創(chuàng)建日期和元數(shù)據(jù)。當(dāng)創(chuàng)建資源的時(shí)候,是對媒體文件的處理。為了高效加載資源,AVAsset 使用了延遲加載資源屬性的方案。不過屬性的訪問總是同步發(fā)生,如果正在請求的屬性沒有預(yù)先加載,程序就會(huì)阻塞。不過 AVAsset 和 AVAssetTrack 提供了異步加載資源屬性的方案。AVAsset 和 AVAssetTrack 都實(shí)現(xiàn)了 AVAsynchronousKeyValueLoading 協(xié)議,可以通過相關(guān)的接口進(jìn)行異步查詢資源的屬性。
// 查詢給定屬性的狀態(tài)
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
// 異步載入一個(gè)給定的屬性
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
NSURL *mp3URL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"]];
AVURLAsset *asset = [AVURLAsset assetWithURL:mp3URL];
[asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler:^{
NSError *error;
AVKeyValueStatus status = [asset statusOfValueForKey:@"tracks" error:&error];
switch (status) {
case AVKeyValueStatusLoaded:
break;
case AVKeyValueStatusLoading:
break;
case AVKeyValueStatusUnknown:
break;
case AVKeyValueStatusFailed:
break;
case AVKeyValueStatusCancelled:
break;
default:
break;
}
}];
- 每次調(diào)用
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;只調(diào)用一次 completionHandler 塊,調(diào)用該方法的次數(shù)并不是根據(jù)傳遞給這個(gè)方法的鍵的個(gè)數(shù)而定的。
- 需要為每個(gè)請求的屬性調(diào)用
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;方法。不能假設(shè)所有的屬性都返回相同的狀態(tài)值。
元數(shù)據(jù)
AVAsset 和 AVAssetTrack 都可以實(shí)現(xiàn)相關(guān)元數(shù)據(jù)的查詢功能。大部分情況下我們會(huì)使用 AVAsset 提供的元數(shù)據(jù),不過涉及獲取曲目一級元數(shù)據(jù)等情況時(shí)也會(huì)使用 AVAssetTrack。讀取具體資源元數(shù)據(jù)的接口名為 AVMetadataItem 的類提供。
// 屬性中包含著當(dāng)前視頻常見格式類型的元數(shù)據(jù)
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *commonMetadata;
// 屬性中包含當(dāng)前視頻所有格式類型的元數(shù)據(jù)
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *metadata NS_AVAILABLE(10_10, 8_0);
// 屬性中包含當(dāng)前視頻所有可用元數(shù)據(jù)的格式類型
元數(shù)據(jù)的格式類型在AVMetadataFormat中定義了很多種,常見的有title、creator、subject、publisher等
@property (nonatomic, readonly) NSArray<NSString *> *availableMetadataFormats;
// 通過format獲取特定格式類型元數(shù)據(jù)
- (NSArray<AVMetadataItem *> *)metadataForFormat:(NSString *)format;
AVMetadata 的相關(guān) Key 值。
AVF_EXPORT NSString *const AVMetadataCommonKeyTitle NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyCreator NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeySubject NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyDescription NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyPublisher NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyContributor NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyCreationDate NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyLastModifiedDate NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyType NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyFormat NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyIdentifier NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeySource NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyLanguage NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyRelation NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyLocation NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyCopyrights NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyAlbumName NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyAuthor NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyArtist NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyArtwork NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyMake NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeyModel NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVMetadataCommonKeySoftware
章節(jié)元數(shù)據(jù)
Asset中有一種特殊的元數(shù)據(jù):章節(jié)。它是AVTimedMetadataGroup類型,這種類型表示一個(gè)只在特定時(shí)間段有效的元數(shù)據(jù)集合,也就是說章節(jié)中所包含的元數(shù)據(jù)只在當(dāng)前章節(jié)的時(shí)間段有效。
// 表示當(dāng)前Asset中可用的章節(jié)Locale
@property (readonly) NSArray<NSLocale *> *availableChapterLocales ;
// 方法通過locale和元數(shù)據(jù)的commonkey篩選出特定的元數(shù)據(jù),這些元數(shù)據(jù)只在當(dāng)前章節(jié)的時(shí)間段有效
- (NSArray<AVTimedMetadataGroup *> *)chapterMetadataGroupsWithTitleLocale:(NSLocale *)locale containingItemsWithCommonKeys:(nullable NSArray<NSString *> *)commonKeys NS_AVAILABLE(10_7, 4_3);
// 方法通過指定一種語言,返回一個(gè)章節(jié)元數(shù)據(jù)數(shù)組。數(shù)組中越匹配指定語言的元數(shù)據(jù),位置越靠前。
- (NSArray<AVTimedMetadataGroup *> *)chapterMetadataGroupsBestMatchingPreferredLanguages:(NSArray<NSString *> *)preferredLanguages NS_AVAILABLE(10_8, 6_0);
媒體選擇
一個(gè)多媒體文件中相同的媒體特征的東西可能會(huì)有很多,比如一個(gè)視頻中可能會(huì)有2種字幕。對于類似選擇哪個(gè)字幕的問題,有以下幾個(gè)API:
// 當(dāng)前asset中有效的媒體特征選項(xiàng)。數(shù)組類型,里面包含著代表相應(yīng)媒體特征的string.
@property (nonatomic, readonly) NSArray<NSString *> *availableMediaCharacteristicsWithMediaSelectionOptions NS_AVAILABLE(10_8, 5_0);
// 通過傳入一個(gè)媒體特征類型,返回可供選擇的媒體選項(xiàng)集合。例如傳入字幕的媒體特征類型,返回當(dāng)前Asset的可供選擇的字幕選項(xiàng)集合。
- (nullable AVMediaSelectionGroup *)mediaSelectionGroupForMediaCharacteristic:(NSString *)mediaCharacteristic NS_AVAILABLE(10_8, 5_0);
// 主要是為各個(gè)媒體選項(xiàng)集合提供默認(rèn)選項(xiàng)。
@property (nonatomic, readonly) AVMediaSelection *preferredMediaSelection NS_AVAILABLE(10_11, 9_0);
參考
AVFoundation開發(fā)秘籍:實(shí)踐掌握iOS & OSX應(yīng)用的視聽處理技術(shù)
源碼地址:AVFoundation開發(fā) https://github.com/QinminiOS/AVFoundation