AVFoundation-02资源

概述

AVFoundation 是一个可以用来使用和创建基于时间的视听媒体数据的框架。AVFoundation 的构建考虑到了目前的硬件环境和应用程序,其设计过程高度依赖多线程机制。充分利用了多核硬件的优势并大量使用block和GCD机制,将复杂的计算机进程放到了后台线程运行。会自动提供硬件加速操作,确保在大部分设备上应用程序能以最佳性能运行。该框架就是针对64位处理器设计的,可以发挥64位处理器的所有优势。

iOS 媒体环境.png

AVAsset

AVFondation 是一个非常强大且可扩展的框架,包括对媒体的捕捉、组合、播放和处理等广泛功能,同时它还有别于传统面向文件的音频类,框架把所有的代码设计围绕着 “资源” 进行。资源最重要的类是 AVAsset,它是 AVFoundation 设计的核心,在几乎所有特性和功能的开发中扮演着至关重要的角色。AVAsset 是一个抽象类,定义了媒体资源混合呈现的方式,将媒体的静态属性模块化成一个整体,比它们的标题、时长、元数据。
AVAsset 不需要考虑媒体资源所具有的两个重要范畴。第一是它提供了对基本媒体格式的层抽象。也就是说无论是处理影片、音频,对开发者和框架而言面对的只有资源这个概念,让开发者面对不同格式的内容的时候有统一的处理方法,不用考虑许多编解码的细节。第二是它隐藏了资源的位置信息。当我们处理资源的时候,可以通过URL来创建资源,这个地址可能在本地、也可能在远程服务器上。

AVAssetTrack

AVAsset 本身不是媒体资源,但是它可以作为时基媒体的容器。它由一个或多个带有描述自身元数据的媒体组成。我们使用 AVAssetTrack 类代表保存在资源中统一类型媒体,并对每个资源建立相应的模型。AVAssetTrack 常见的形态是音频和视频流,但是它还可以表示文本、副标题、隐藏字幕等媒体类型。

AVAsset 的组成.png

在 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容