概述
AVFoundation 是一个可以用来使用和创建基于时间的视听媒体数据的框架。AVFoundation 的构建考虑到了目前的硬件环境和应用程序,其设计过程高度依赖多线程机制。充分利用了多核硬件的优势并大量使用block和GCD机制,将复杂的计算机进程放到了后台线程运行。会自动提供硬件加速操作,确保在大部分设备上应用程序能以最佳性能运行。该框架就是针对64位处理器设计的,可以发挥64位处理器的所有优势。
AVAsset
AVFondation 是一个非常强大且可扩展的框架,包括对媒体的捕捉、组合、播放和处理等广泛功能,同时它还有别于传统面向文件的音频类,框架把所有的代码设计围绕着 “资源” 进行。资源最重要的类是 AVAsset,它是 AVFoundation 设计的核心,在几乎所有特性和功能的开发中扮演着至关重要的角色。AVAsset 是一个抽象类,定义了媒体资源混合呈现的方式,将媒体的静态属性模块化成一个整体,比它们的标题、时长、元数据。
AVAsset 不需要考虑媒体资源所具有的两个重要范畴。第一是它提供了对基本媒体格式的层抽象。也就是说无论是处理影片、音频,对开发者和框架而言面对的只有资源这个概念,让开发者面对不同格式的内容的时候有统一的处理方法,不用考虑许多编解码的细节。第二是它隐藏了资源的位置信息。当我们处理资源的时候,可以通过URL来创建资源,这个地址可能在本地、也可能在远程服务器上。
AVAssetTrack
AVAsset 本身不是媒体资源,但是它可以作为时基媒体的容器。它由一个或多个带有描述自身元数据的媒体组成。我们使用 AVAssetTrack 类代表保存在资源中统一类型媒体,并对每个资源建立相应的模型。AVAssetTrack 常见的形态是音频和视频流,但是它还可以表示文本、副标题、隐藏字幕等媒体类型。
在 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中包含了当前 AVAsset 中的所有track,通过遍历我们可以获得想要的track。- (NSArray<AVAssetTrack *> *)tracksWithMediaType:(NSString *)mediaType;
方法会根据指定的媒体类型返回一个track数组,数组中包含着Asset中所有指定媒体类型的track。如果Asset中没有这个媒体类型的track,返回一个空数组。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;
方法会根据指定的媒体特征返回track数组。如果 AVAsset 中没有这个媒体特征的track,返回空数组。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;
创建资源
AVAsset 是一个抽象类,不能直接实例化。当使用 assetWithURL: 方法创建实例的时候,实际上创建的是它的子类,子类为AVURLAsset 。
NSURL *mp3URL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"]];
AVAsset *asset = [AVAsset assetWithURL:mp3URL];
我们也可以直接创建 AVURLAsset 实例,我们可以传递更多的参数,来更精确地获取计时相关的信息。当然这样做可能需要加载更长的时间,以便获取更准确的时长及时间信息。
NSDictionary *dict = @{
AVURLAssetPreferPreciseDurationAndTimingKey : @(YES)
};
NSURL *mp3URL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp3"]];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:mp3URL options:dict];
照片库
用户使用相机或者第三方视频捕捉程序捕捉的视频,它们通常被保存在用户的照片库中。我们可以通过 AssetsLibrary 来访问照片,并创建 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 具有多种有用的方法和属性,可以提供有关的资源信息,比如时长、创建日期和元数据。当创建资源的时候,是对媒体文件的处理。为了高效加载资源,AVAsset 使用了延迟加载资源属性的方案。不过属性的访问总是同步发生,如果正在请求的属性没有预先加载,程序就会阻塞。不过 AVAsset 和 AVAssetTrack 提供了异步加载资源属性的方案。AVAsset 和 AVAssetTrack 都实现了 AVAsynchronousKeyValueLoading 协议,可以通过相关的接口进行异步查询资源的属性。
// 查询给定属性的状态
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
// 异步载入一个给定的属性
- (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;
}
}];
- 每次调用
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
只调用一次 completionHandler 块,调用该方法的次数并不是根据传递给这个方法的键的个数而定的。
- 需要为每个请求的属性调用
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
方法。不能假设所有的属性都返回相同的状态值。
元数据
AVAsset 和 AVAssetTrack 都可以实现相关元数据的查询功能。大部分情况下我们会使用 AVAsset 提供的元数据,不过涉及获取曲目一级元数据等情况时也会使用 AVAssetTrack。读取具体资源元数据的接口名为 AVMetadataItem 的类提供。
// 属性中包含着当前视频常见格式类型的元数据
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *commonMetadata;
// 属性中包含当前视频所有格式类型的元数据
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *metadata NS_AVAILABLE(10_10, 8_0);
// 属性中包含当前视频所有可用元数据的格式类型
元数据的格式类型在AVMetadataFormat中定义了很多种,常见的有title、creator、subject、publisher等
@property (nonatomic, readonly) NSArray<NSString *> *availableMetadataFormats;
// 通过format获取特定格式类型元数据
- (NSArray<AVMetadataItem *> *)metadataForFormat:(NSString *)format;
AVMetadata 的相关 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
章节元数据
Asset中有一种特殊的元数据:章节。它是AVTimedMetadataGroup类型,这种类型表示一个只在特定时间段有效的元数据集合,也就是说章节中所包含的元数据只在当前章节的时间段有效。
// 表示当前Asset中可用的章节Locale
@property (readonly) NSArray<NSLocale *> *availableChapterLocales ;
// 方法通过locale和元数据的commonkey筛选出特定的元数据,这些元数据只在当前章节的时间段有效
- (NSArray<AVTimedMetadataGroup *> *)chapterMetadataGroupsWithTitleLocale:(NSLocale *)locale containingItemsWithCommonKeys:(nullable NSArray<NSString *> *)commonKeys NS_AVAILABLE(10_7, 4_3);
// 方法通过指定一种语言,返回一个章节元数据数组。数组中越匹配指定语言的元数据,位置越靠前。
- (NSArray<AVTimedMetadataGroup *> *)chapterMetadataGroupsBestMatchingPreferredLanguages:(NSArray<NSString *> *)preferredLanguages NS_AVAILABLE(10_8, 6_0);
媒体选择
一个多媒体文件中相同的媒体特征的东西可能会有很多,比如一个视频中可能会有2种字幕。对于类似选择哪个字幕的问题,有以下几个API:
// 当前asset中有效的媒体特征选项。数组类型,里面包含着代表相应媒体特征的string.
@property (nonatomic, readonly) NSArray<NSString *> *availableMediaCharacteristicsWithMediaSelectionOptions NS_AVAILABLE(10_8, 5_0);
// 通过传入一个媒体特征类型,返回可供选择的媒体选项集合。例如传入字幕的媒体特征类型,返回当前Asset的可供选择的字幕选项集合。
- (nullable AVMediaSelectionGroup *)mediaSelectionGroupForMediaCharacteristic:(NSString *)mediaCharacteristic NS_AVAILABLE(10_8, 5_0);
// 主要是为各个媒体选项集合提供默认选项。
@property (nonatomic, readonly) AVMediaSelection *preferredMediaSelection NS_AVAILABLE(10_11, 9_0);
参考
AVFoundation开发秘籍:实践掌握iOS & OSX应用的视听处理技术
源码地址:AVFoundation开发 https://github.com/QinminiOS/AVFoundation