AVFoundation之AVAsset

AVAsset是AVFoundation最重要的类,框架把所有代码设计围绕资源(assert)进行,可以说AVFoundation就是面向资源开发的世界。

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;

AVAsset是一个抽象不可变类,把媒体所有静态属性模块化成为一个整体,比如:标题,时长,元数据等, AVAsset不管你从URL还是照片库获取到媒体,它都会统一管理。
我们使用 AVAssetTrack 类代表保存在资源中的统一类型媒体,并对每个资源建立相应的模型。AVAssetTrack 最常见的形态就是音频和视频流,但是它还可以表示文本、副标题或隐藏字幕等媒体类型。
资源创建:

NSURL *assetURL = [NSURL URLWithString:@""];
AVAsset *asset = [AVAsset assetWithURL:assetURL];

AVAsset 是一个抽象类,意味着不能直接被实例化。当创建实例时,实际上是创建了它子类的一个实例,子类名为 AVURLAsset。有的时候我们会直接使用 AVURLAsset 这个类,因为它允许通过字典来调整资源的创建方式。比如允许程序稍长一点的加载时间,获取更准确的时长和时间信息。

NSURL *assetURL = [NSURL URLWithString:@""];

NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @YES };

AVURLAsset *asset = [AVURLAsset URLAssetWithURL:assetURL options:options];
元数据格式

Apple 环境下的媒体类型主要有四种
QuickTime( mov )
MPEG-4 video( mp4 和 m4v )
MPEG-4 audio( m4a )
MPEG-Layer III audio( mp3 )

QuickTime 是由苹果公司开发的一种功能强大、跨平台的媒体架构。
mp4 是对 MPEG-4 媒体的标准扩展,m4v、m4a、m4p、m4b 这些变体都使用 MPEG-4 容器格式,但是包含了附加的拓展功能。m4v 文件是带有针对 FairPlay 加密和 AC3-audio 扩展的 MPEG-4 视频格式,如果不涉及这些 mp4 和 m4v 仅仅是扩展名不同而已。m4a 针对音频,让使用者知道该文件只带有音频资源。m4p 是苹果较旧的 iTunes 音频格式。m4b 用于有声读物,通常包含章节标签和书签功能。
mp3 文件和上面两种有显著区别,mp3 文件不使用容器格式,而使用编码音频数据。mp3 文件使用一种称为 ID3v2 格式来保存关于音频内容的描述信息,包含的数据有歌曲演唱者、所属唱片和音乐风格等。AV Foundation 支持读取 ID3v2 标签的所有版本,但是不支持写入,所以 AV Foundation 无法支持对 mp3 进行编码。

异步载入

AVAsset使用一种高效的设计方法,延迟载入资源的属性,什么时候使用,什么时候再加载.虽然这可以解决一些由于直接加载数据带来的问题,但是访问AVAsset的属性如果耗时较长,而又发生在主线程,就会阻塞主线程,使界面无法响应.属性的访问总是同步发生的,如果没有预先载入,程序就会阻塞(我测了一下查询资源的availableMetadataFormats大概花了2秒左右),所以这时候就需要异步查询资源的属性。
所以AVAsset和AVAssetTrack都遵守了AVAsynchronousKeyValueLoading协议,通过下面两个方法实现异步查询

   -(AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * __nullable * __nullable)outError;
 -(void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;

通过statusOfValueForKey方法查询一个给定属性的状态,该方法会返回一个枚举类型的AVKeyValueStatus值,用于标识查询的属性的状态,如果状态不是AVKeyValueStatusLoaded,意味着此时请求该属性会导致程序卡顿。
通过调用loadValuesAsynchronouslyForKeys: completionHandler:方法异步查询属性,为其提供一个属性的数组,当资源处于回应请求状态就会调用completionHandler代码块

 AVAsset *asset = [AVAsset assetWithURL:assetUrl];
    NSArray *keys = @[@"tracks",
                      @"playable",
                      @"duration"];

    __weak typeof(asset) weakAsset = asset;
    __weak typeof(self) weakSelf = self;
    [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
            // check the keys
            for (NSString *key in keys) {
                NSError *error = nil;
                AVKeyValueStatus keyStatus = [weakAsset statusOfValueForKey:key error:&error];

                switch (keyStatus) {
                    case AVKeyValueStatusFailed:{
                        // failed
                        break;
                    }
                    case AVKeyValueStatusLoaded:{
                        // success
                        break;
                    }case AVKeyValueStatusCancelled:{
                        // cancelled
                        break;
                    }
                    default:
                        break;
                }
        }
    }];
  • 使用元数据

AVAsset 和 AVAssetTrack 都可以实现查询相关元数据的功能。一般使用 AVAsset 提供的元数据,当涉及获取曲目一级元数据等情况时会使用 AVAssestTrack。读取具体资源元数据的接口由 AVMetadataItem 的类提供。


17E.png

上图中common部分表示,歌名,歌手,插图等信息,AVFoundation使用键空间(key spaces)作为将相关键组合在一起的方法,比如上图common它表示的歌手歌名就是每种格式的文件都会有的信息。

  • 我们可以通过查询资源(AVAsset)或曲目(AVAssetTrack)的commonMetadata属性从Common键空间获取元数据(歌手歌名等信息),这个属性会返回一个包含所有可用元数据的数组.
  • 通过availableMetedataFormats键来查询资源中所有的元数据的格式(mp4,mp3,mov等),比如com.apple.itunes代表itunes类型文件(.mp4 m4v .m4a)。并给每个格式调用metadataForFormat:方法获指定取元数据,这个方法返回一个包含所有相关元数据信息的NSArray.
    看一个例子
AVAsset *asset = [AVAsset assetWithURL:url];
    [asset loadValuesAsynchronouslyForKeys:@[@"availableMetadataFormats"] completionHandler:^{
        NSMutableArray *arr = [NSMutableArray array];
        //如果直接调用这个asset.availableMetadataFormats是同步的,会卡界面,上面的loadValuesAsynchronouslyForKeys:方法就是先异步加载好这个availableMetadataFormats属性
        for (NSString *str in asset.availableMetadataFormats) {
            [arr addObject:[asset metadataForFormat:str]];
            NSLog(@"%@",[asset metadataForFormat:str]);
        }
    }];

查找元数据

使用metadataItemsFromArray: withKey: keySpace:从一堆元数据数组中筛选出需要的一个,虽然返回是一个数组,但是其实里面通常只有一个元素
当我们拿到一个元数据的数组时,使用AVMetadataItem获取具体元数据的值。
例如:想要拿到一个m4a音频文件的歌手信息,需要用如下方法:

    NSArray *artistMetadata = [AVMetadataItem metadataItemsFromArray:arr2[0] withKey:AVMetadataiTunesMetadataKeyArtist keySpace:AVMetadataKeySpaceiTunes];

使用AVMetadataItem

AVMetadataItem最基本的形式其实是一个封装键值对的封装器
可通过它查询key或commonKey,查询其是否存在于Common键空间中,最重要的是它对应的value.
value属性被定义成id<NSObject,NSCopying>形式,不过它可能是NSString,NSNumber,NSData或者NSDictionary等.

    AVAsset *asset = [AVAsset assetWithURL:url];
    NSArray *arr = @[@"availableMetadataFormats"];
    NSMutableArray *arr2 = [NSMutableArray array];
    [asset loadValuesAsynchronouslyForKeys:arr completionHandler:^{
        NSError *error=nil;
        //如果直接调用这个asset.availableMetadataFormats是同步的,会卡界面,上面的loadValuesAsynchronouslyForKeys:方法就是先异步加载好这个availableMetadataFormats属性,然后现在就可以直接读了。这是apple的优化。
        AVKeyValueStatus state=[asset statusOfValueForKey:@"availableMetadataFormats" error:&error];
        switch (state) {
            case AVKeyValueStatusLoaded:
               for (NSString *format in asset.availableMetadataFormats)
                {
                    
                    NSArray *meatataArray=[asset metadataForFormat:format];
                    NSArray *artisArray=[AVMetadataItem metadataItemsFromArray:meatataArray withKey:AVMetadataCommonKeyArtist keySpace:AVMetadataKeySpaceCommon];
                    for (AVMetadataItem *item in artisArray)
                    {
                        NSLog(@"key=%@,value=%@",item.key,item.value);
                        dispatch_async(dispatch_get_main_queue(), ^{
//                            
                        });
                        
                    }
                }
                
                NSLog(@"%@",@"加载成功");
                break;
            case AVKeyValueStatusFailed:
                NSLog(@"%@",@"加载失败");
                break;
            default:
                break;
        }
    }];

大部分开发者第一次遇到AVMetadataItem都会碰到一个常见问题是:如何理解该对象的key属性,打开AVMetadaFormat.h文件,一看就容易明白。

保存元数据

前面提到过,AVAsset是个不可以变化的类,我们不能修改,我们要使用AVAssetExportSession的类来导出一个新的资源副本以及元数据的改动。AVAssetExportSession是用于将AVAsset内容根据导出预设进行编码,并将导出资源写到磁盘中。有兴趣大家可以去研究下。

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

推荐阅读更多精彩内容