1-AVAsset资源和元数据篇

1.AVAsset 资源

AV Foundation最重要的类就是AVAsset,也是AV Foundation设计的核心。AVAsset是一个抽象类和不可变类,定义了媒体资源混合呈现的方式,将媒体资源的静态属性模块化成一个整体,比如它们的标题,时长和元数据等。
AVAsset提供了对基本媒体格式的层抽象,这意味着无论是处理QuickTime影片,MPEG-4视频还是MP3音频,对你和对框架其余部分而言,面对的只要资源的概念.不需要考虑多种编解码器和容器格式因为细节不同而带来的困扰.
AVAsset本身不是媒体资源,但是它可以作为时基媒体的容器.它由一个或多个带有描述自身元数据的媒体组成.我们使用AVAssetTrack类代表保存在资源中的统一类型媒体,并对每个资源建立相应的模型.AVAssetTrack最常见的形态就是音频和视频流,但是它还可以用于表示诸如文本,副标题或隐藏字幕等媒体类型.

  • AVAsset属性和方法的简单介绍
//视频时长
@property (nonatomic, readonly) CMTime duration;
//默认的速度
@property (nonatomic, readonly) float preferredRate;
//默认音量
@property (nonatomic, readonly) float preferredVolume;
//commonMetadata属性中包含着当前视频常见格式类型的元数据
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *commonMetadata;
//metadata属性中包含当前视频所有格式类型的元数据
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *metadata;
//availableMetadataFormats属性中包含当前视频所有可用元数据的格式类型元数据的格式类型在AVMetadataFormat中定义了很多种,常见的有title、creator、subject、publisher等
@property (nonatomic, readonly) NSArray<NSString *> *availableMetadataFormats;
//此资源中包含的所有曲目(AVAssetTrack),AVAsset还可以通过标识符,媒体类型或媒体特征等信息找到相应的曲目.
@property (nonatomic, readonly) NSArray*tracks;
//通过trackID获得trackAVAssetTrack
-(nullable AVAssetTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;
//通过指定的媒体类型返回一个AVAssetTrack数组,数组中包含着Asset中所有指定媒体类型的AVAssetTrack。如果Asset中没有这个媒体类型的AVAssetTrack,返回一个空数组
-(NSArray<AVAssetTrack *> *)tracksWithMediaType:(NSString *)mediaType;
//通过指定的媒体特征返回AVAssetTrack数组,数组的特性与-tracksWithMediaType:类似,如果Asset中没有这个媒体特征的AVAssetTrack,返回一个空数组。
-(NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;

2.创建资源实例

当你为一个现有媒体资源创建AVAsset对象时,可以通过URL对它进行初始化来实现.URL可以是本地资源的,也可以是远程资源的.

NSURL *asstUrl=[[NSBundle mainBundle]URLForResource:@"Hubblecast" withExtension:@"mov"];
    AVAsset *asset=[AVAsset assetWithURL:asstUrl];

AVAsset是一个抽象类,意味着它不可以被直接实例化,当使用它的assetWithURL:方法创建实例时,实际上是创建其子类(AVURLAsset)的实例.
如果创建一个用在音频或者视频编辑场景中的资源,可能希望传递一个选项(option)来告诉程序提供更精确的时长和计时信息,传递选项就暗示了开发者希望得到稍长一点的加载时间,以获取更精确的时长和计时信息.

NSURL *asstUrl=[[NSBundle mainBundle]URLForResource:@"Hubblecast" withExtension:@"mov"];
    NSDictionary *option=@{AVURLAssetPreferPreciseDurationAndTimingKey:@YES};
    AVURLAsset *urlAsset=[[AVURLAsset alloc]initWithURL:asstUrl options:option];

3.异步载入(异步加载)

AVAsset具有多种方法和属性,可以提供有关资源的信息,比如时长,创建日期和元数据等.
AVAsset还包含一些用于获取和使用曲目集合的方法.
AVAsset使用一种高效的设计方法,即延迟载入资源的属性,直到请求时才载入.
AVAsset和AVAssetTrack都采了AVAsynchronousKeyValueLoading协议,该协议通过以下方法实现异步查询属性的功能.

//异步载入一个或多个资源的属性名,当资源处于回应请求时调用block块.
-(void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler NS_AVAILABLE(10_7, 4_2);
//查询一个给定属性的状态,该方法返回一个枚举类型的AVKeyValueStatus值,用于表示当前所请求的属性的状态.
-(AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * __nullable * __nullable)outError NS_AVAILABLE(10_7, 4_2);
  • 示例代码:
NSURL *asstUrl=[[NSBundle mainBundle]URLForResource:@"Hubblecast" withExtension:@"mov"];
    AVAsset *asset=[AVAsset assetWithURL:asstUrl];
    
    NSArray *keys=@[@"availableMetadataFormats"];
    [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
        NSError *error=nil;
        AVKeyValueStatus state=[asset statusOfValueForKey:@"availableMetadataFormats" error:&error];
        switch (state) {
            case AVKeyValueStatusLoaded:
                NSLog(@"%@",@"加载成功");
                break;
                case AVKeyValueStatusFailed:
                NSLog(@"%@",@"加载失败");
                break;
            default:
                break;
        }
    }];
  • 注意
    1. 每次调用loadValuesAsynchronouslyForKeys: completionHandler:方法是只会调用一次completionHandler块,调用该方法的次数并不是根据传递给这个方法的键的个数而定的.
    2. 需要为每个请求的属性调用tatusOfValueForKey:error:方法,不能假设所有的属性都返回相同的状态值.

4.媒体元数据

  • AVAsset和AVAssetTrack都可以实现查询相关元数据的功能,一般情况下使用AVAsset提供的元数据,不过当涉及获取曲目一级元数据等情况时也会使用AVAssetTrack.
  • 读取具体资源元数据的接口由名为AVMetadataItem的类提供.
    AVMetadataItem类提供了一个面向对象的接口,让我们可以对存储于QuickTime,MPEG-4 atom和ID3帧中的元数据进行访问.
  • AVAsset和AVAssetTrack提供了两种方法可以获取相关的元数据.
    要了解这两种方法的适用范围,首先要知道键空间(key spaces)的含义.
  • AVFoundation使用键空间作为将相关键组合在一起的方法,可以实现对AVMetadataItem实例集合的筛选.每个资源至少包含两个键空间,供从其中获取数据.
  • Common键空间用来定义所有支持的媒体类型的键,包括诸如曲名,歌手和插图信息等常见元素.这提供了一种对所有支持的媒体格式进行一定级别的元数据标准化的过程.
  • 我们可以通过查询资源(AVAsset)或曲目(AVAssetTrack)的commonMetadata属性从Common键空间获取元数据,这个属性会返回一个包含所有可用元数据的数组.
@property (nonatomic, readonly) NSArray<AVMetadataItem *> *commonMetadata;
  • 访问指定格式的元数据需要在资源或曲目上调用metadataForFormat:方法.这个方法包含一个用于定义元数据格式的NSString对象并返回一个包含所有相关元数据信息的NSArray.
  • 元数据格式的NSString对象可以通过查询资源或曲目的availableMetadataFormats属性获得,其返回一个字符串数组,其中定义了该资源中包含的所有元数据格式,我们可以利用这个结果循环访问所有格式,并为每种格式调用metadataForFormat:方法.
@property (nonatomic, readonly) NSArray<NSString *> *availableMetadataFormats;
-(NSArray<AVMetadataItem *> *)metadataForFormat:(NSString *)format;

5.查找元数据

  • 当我们得到一个包含元数据项的数组时,通常希望找到所需的具体元数据值.
    可以使用AVMetadataItem类提供的便利方法,来获取结果集合并对其进行筛选.
  • 使用metadataItemsFromArray: withKey: keySpace:方法来对集合进行筛选,得出那些匹配键和键空间标准的对象.这个方法返回值是一个NSArray,但通常只包含单个AVMetadataItem实例.
  • 例子
NSArray *meatataArray=[asset metadataForFormat:format];
NSString *keySpace=AVMetadataKeySpaceCommon;
NSString *artisKey=AVMetadataCommonKeyArtwork;
 NSArray *artisArray=[AVMetadataItem metadataItemsFromArray:meatataArray withKey:artisKey keySpace:keySpace];

6.使用AVMetadataItem

  • AVMetadataItem最基本的形式其实是一个封装键值对的封装器.
  • 可通过它查询key或commonKey,查询其是否存在于Common键空间中,最重要的是它对应的value.
  • value属性被定义成id<NSObject,NSCopying>形式,不过它可能是NSString,NSNumber,NSData或者NSDictionary等.
  • 代码
NSURL *asstUrl=[[NSBundle mainBundle]URLForResource:@"Hubblecast" withExtension:@"mov"];
    AVAsset *asset=[AVAsset assetWithURL:asstUrl];
    NSArray *keys=@[@"availableMetadataFormats"];
    [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
        NSError *error=nil;
        AVKeyValueStatus state=[asset statusOfValueForKey:@"availableMetadataFormats" error:&error];
        switch (state) {
            case AVKeyValueStatusLoaded:
                for (NSString *format in asset.availableMetadataFormats)
                {
                    
                    NSArray *meatataArray=[asset metadataForFormat:format];
                    NSString *keySpace=AVMetadataKeySpaceCommon;
                    NSString *artisKey=AVMetadataCommonKeyArtwork;
                    NSArray *artisArray=[AVMetadataItem metadataItemsFromArray:meatataArray withKey:artisKey keySpace:keySpace];
                    for (AVMetadataItem *item in meatataArray)
                    {
                        dispatch_async(dispatch_get_main_queue(), ^{
                         self.imageView.image=[UIImage imageWithData:item.dataValue];
                        });

                    }
                }

                NSLog(@"%@",@"加载成功");
                break;
                case AVKeyValueStatusFailed:
                NSLog(@"%@",@"加载失败");
                break;
            default:
                break;
        }
    }];

7.保存修改之后的元数据

  • AVAsset是一个不可变类,我们不能直接修改它.而是使用名为AVAssetExportSession的类导出一个新的资源副本以及元数据改动.
  • 应用AVAssetExportSession
    • AVAssetExportSession用于将AVAsset内容根据导出预设条件进行转码,并将导出资源写到磁盘中.
    • AVAssetExportSession提供了多个功能来实现将一种格式转换为另一种格式,修订资源的内容,修改资源的音频和视频行为,还有写入新的元数据.
    • 创建一个AVAssetExportSession实例需要提供资源和导出预设.导出预设用于确定导出内容的质量,大小等属性.创建一个导出会话后,并且需要指定一个outputURL用于声明导出内容将要写入的地址.并且要给出一个outputFileType值来表示将要写入的导出格式.最后调用exportAsynchronouslyWithCompletionHandler:方法开始导出过程.
    • 代码
//AVAssetExportPresetPassthrough预设值,可以让我们在不需要重新对媒体编码的前提下实现写入数据的功能.
 NSString *presetName = AVAssetExportPresetPassthrough;                  
AVAssetExportSession *session =
        [[AVAssetExportSession alloc] initWithAsset:self.asset
                                         presetName:presetName];
    NSURL *outputURL = [self tempURL];                                      
    session.outputURL = outputURL;//写入磁盘(导出)的URL
    session.outputFileType = self.filetype;//导出格式
    session.metadata = [self.metadata metadataItems];//需要导出的元数据                       
    [session exportAsynchronouslyWithCompletionHandler:^{
//判断导出状态
        AVAssetExportSessionStatus status = session.status;
        BOOL success = (status == AVAssetExportSessionStatusCompleted);
        if (success) {                                                      
            NSURL *sourceURL = self.url;
            NSFileManager *manager = [NSFileManager defaultManager];
            [manager removeItemAtURL:sourceURL error:nil];
            [manager moveItemAtURL:outputURL toURL:sourceURL error:nil];                                                      
        } 
    }];
-(NSURL *)tempURL {
    NSString *tempDir = NSTemporaryDirectory();
    NSString *ext = [[self.url lastPathComponent] pathExtension];
    NSString *tempName = [NSString stringWithFormat:@"temp.%@", ext];
    NSString *tempPath = [tempDir stringByAppendingPathComponent:tempName];
    return [NSURL fileURLWithPath:tempPath];
}

8.备用媒体AVMediaSelectionOption,AVMediaSelectionGroup

  • AVMediaSelectionOption表示AVAsset中的备用媒体呈现方式.一个资源可能包含备用媒体呈现方式,比如备用音频,视频或文本轨道.这些轨道可能是指定语言的音频轨道,备用相机角度或指定语言的字幕.
//表示此资源中有哪些备用轨道,返回一个字符串数组,这些字符串表示保存在资源中可用选项的媒体特征.
//具体来说,返回数组包含的字符串值为AVMediaCharacteristicVisual(视频),AVMediaCharacteristicAudible(音频),AVMediaCharacteristicLegible(字幕或隐藏式字幕)
@property (nonatomic, readonly) NSArray<NSString *> *availableMediaCharacteristicsWithMediaSelectionOptions
  • 请求可用媒体特征数据后,调用AVAsset的mediaSelectionGroupForMediaCharacteristic:方法,为其传递要检索的选项的特定媒体特征,这个方法返回一个AVMediaSelectionGroup,它作为一个或多个互斥的AVMediaSelectionOption实例的容器;
    (方法通过传入一个媒体特征类型,返回可供选择的媒体选项集合。例如传入字幕的媒体特征类型,返回当前Asset的可供选择的字幕选项集合。)
-(nullable AVMediaSelectionGroup *)mediaSelectionGroupForMediaCharacteristic:(NSString *)mediaCharacteristic
  • 简单示例1
NSArray *mediaCharacteristics=asset.availableMediaCharacteristicsWithMediaSelectionOptions;
  for (NSString * Characteristic in mediaCharacteristics) {
       AVMediaSelectionGroup *group=[asset mediaSelectionGroupForMediaCharacteristic:Characteristic];
       for (AVMediaSelectionOption *option in group.options) {
              NSLog(@"option:%@",option.displayName);
          }
      }
  • 简单示例2
AVMediaSelectionGroup *group=[asset mediaSelectionGroupForMediaCharacteristic:Characteristic];
NSLocale *russianLocal=[[NSLocale alloc]initWithLocaleIdentifier:@"ru_RU"];
 NSArray *options=[AVMediaSelectionGroup mediaSelectionOptionsFromArray:group.options withLocale:russianLocal];
AVMediaSelectionOption *option=[options firstObject];
  • Asset中默认的媒体选项
//preferredMediaSelection属性是AVMediaSelection类型,他的作用是主要是为各个媒体选项集合提供默认选项
@property (nonatomic, readonly) AVMediaSelection *preferredMediaSelection
- 代码
for (NSString *characteristic in asset.availableMediaCharacteristicsWithMediaSelectionOptions) {
        AVMediaSelectionGroup *group = [asset mediaSelectionGroupForMediaCharacteristic:characteristic];
        AVMediaSelectionOption *option = [asset.preferredMediaSelection selectedMediaOptionInMediaSelectionGroup:group];
        NSLog(@"对应媒体特征%@的默认媒体选项是%@",characteristic,option);
    }

9.待续

推荐阅读更多精彩内容

  • 通过使用资源对象,并通过构建它的元数据编辑来了解AVFoundation的特性. 1. 媒体资源AVAsset A...
    ValienZh阅读 1,490评论 7 3
  • AVPlayerItem 是对于被AVPlayer对象播放的asset进行模拟和记时,它提供了接口访问媒体的不同时...
    木木有耳阅读 5,792评论 5 4
  • AVAsset是AVFoundation最重要的类,框架把所有代码设计围绕资源(assert)进行,可以说AVFo...
    丶丶夏天阅读 1,748评论 0 1
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 25,211评论 30 471
  • 看着,这是你的未来 摊开生命的尺度 只容接受时间的审判 你一路追随着我 如同渴求某种命定的引领 你看着 穿行与沉沦...
    佩索莎Z阅读 30评论 0 0