这篇总结什么?git
在该系列的上一篇的文章中,咱们总结的大体内容以下:github
一、视频录制 AVCaptureSession + AVCaptureMovieFileOutput数组
二、视频录制 AVCaptureSession + AVAssetWriter数据结构
三、AVCaptureSession + AVCaptureMovieFileOutput 与 AVCaptureSession + AVAssetWriter 的区别架构
这是这个系列总结文章的第三篇,前面咱们提了音频以及视频的基本的播放,录制等等的知识,这篇文章咱们总结开发秘籍中的第三章的内容 -- 资源和元数据。 异步
说白了就是总结 AVAsset 这个类!ide
AVAsset性能
AVAsset是一个抽象类(抽象类中不必定包含抽象方法,可是包含抽象方法的类必定要被声明为抽象类。抽象类自己不具有实际的功能,只能用于派生其子类。抽象类中能够包含构造方法,可是构造方法不能被声明为抽象,简单点的说你不能实例化一个抽象类。然而,咱们能够尝试复制该方案在Objective-C中采用一些技巧,要确保不能实例化你的父类),咱们前面简单的说明了一下什么是抽象类,咱们的AVAsset就是一个抽象类,你经过 assetWithURL 实际建立的就是他的子类,名为 AVURLAsset ,这一段话你们仔细理解一下。优化
一:AVAsset的异步载入 AVAsynchronousKeyValueLoading 协议ui
这个AVAsynchronousKeyValueLoading咱们的AVAsset类是遵照了的,这个协议里面就两个必须实现的方法,咱们解释一下这两个方法:
/* typedef NS_ENUM(NSInteger, AVKeyValueStatus) { AVKeyValueStatusUnknown, AVKeyValueStatusLoading, AVKeyValueStatusLoaded, AVKeyValueStatusFailed, AVKeyValueStatusCancelled }; // 这个方法能够用来查询给定属性的状态,若是返回的这个状态不是AVKeyValueStatusLoaded,那咱们在此刻去请求这个状态的时候可能会出现卡顿 - (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError; // keys参数就是咱们要请求的属性数组,当完成请求以后就会在handler这个block回调给咱们 - (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler; */
咱们简单的应用一下上面的知识,写个很简单的Demo,这个Demo仍是会在咱们这一系列文章的git上,咱们请求一些咱们本地数据的一些基本的属性,代码以下:
-(void)getAssetMessage{ NSString * path = [[NSBundle mainBundle]pathForResource:@"薛之谦-像风同样.mp3" ofType:nil]; NSURL * url = [NSURL fileURLWithPath:path]; AVAsset * asset = [AVAsset assetWithURL:url]; NSArray * keys = @[@"duration"]; [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ NSError * error; AVKeyValueStatus status = [asset statusOfValueForKey:@"duration" error:&error]; switch (status) { case AVKeyValueStatusLoaded: // 要更新UI的操做须要回到主线程 NSLog(@"属性载入成功,你能够访问了"); NSLog(@"duration = %.2f",CMTimeGetSeconds(asset.duration)); break; case AVKeyValueStatusLoading: NSLog(@"AVKeyValueStatusLoading"); break; case AVKeyValueStatusFailed: NSLog(@"AVKeyValueStatusFailed"); break; case AVKeyValueStatusUnknown: NSLog(@"AVKeyValueStatusUnknown"); break; default: break; } }]; }
上面的输出的日志以下:
须要注意的地方在代码注释中有些,通过上面的代码咱们就异步的访问了它的duration属性,为何咱们访问一个属性都须要写这些个代码呢?咱们说一下缘由为这个AVAsynchronousKeyValueLoading协议的总结画一个句号。
说明: 咱们之因此须要异步的访问一些属性,是由于属性的访问总结同步的发生的,若是正在请求的属性没有预先载入,程序就会阻塞,一直到它能够作出适当的响应,显然这样必定会带来问题,好比咱们上面说的duration属性可能就是一个潜在的昂贵操做,若是开发者在使用MP3文件时候没有在头文件中设置TLEN标签,这个标签用于定义duration值,则整个音频曲目都须要进行解析来准确肯定它的duration值,假设这个请求发生在主线程,那么等待响应就会阻塞主线程,直到相关的操做完成为止,在最好的状况下可能会感受应用变得迟钝,用户界面没有响应。
媒体元数据
元数据的格式:
虽然存在不少种格式的媒体资源,可是咱们在iOS的环境下遇到的媒体的类型主要就是下面的四类,咱们简单的总结一下下面的四类,就再也不作具体的说明,有兴趣的研究这些类型的能够本身上网查查:
一:QuickTime
QuickTime 是由苹果开发的一种功能强大、跨平台的媒体架构。该架构的一部分是 QuickTime File Formant 规范, 定义了 .mov文件的内部结构。 QuickTime 文件由一种称为 atoms 的数据结构组成。
二:MPEG-4 音频和视频
MPEG-4 Part 14 是定义MP4文件格式的规范,MP4直接派生于 QuickTime 文件格式,这就意味着它与 QuickTime 文件的结构是相似的,就像QuickTime文件同样,MP4文件也由称为 atom 的数据结构组成。 关于文件名再说一点, .mp4 是对MPEG-4媒体的标准扩展。但存在一些变化,如 .m4v、.m4a、.m4p 、 .m4b 等,这些变体都是使用的 MPEG-4 容器格式,但包含了附加的扩展功能。
三:MP3
MP3文件与上面介绍的两种格式有显著的区别,MP3文件使用容器格式,而使用编码音频数据,包含的可选元数据的结构块一般位于文件开头。MP3文件使用一种称为ID3v2的格式来保存关于音频内容的描述信息,包含的数据有歌曲演唱者、所属唱片和音乐风格等等。
AV Foundation 支持读取ID3v2标签的全部版本,但不支持写入。MP3格式收到专利限制,因此 AVFoundation 没法支持对MP3后者ID3数据进行编码。
使用元数据
在大部分状况下咱们会使用 AVAsset 提供的元数据,不过设计获取曲目以及原数据等状况时候也会使用 AVAssetTrack , 读取具体的资源元数据的接口由 AVMetadataItem 这个类提供,这个类提供了一个面向对象的接口,让开发这能够对存储在 QuickTime、MPeg-4 atom、ID3 帧中的元数据进行访问。
说一下 AVAsset 的三个属性/方法:
一、commonMetadata 这个属性从Common键空间获取元数据,这个属性会返回以一个包含全部可用元数据的数组
二、availableMetadataFormats 这个属性会返回一个字符串数组,其中定义了资源中包含的全部的原数据格式
三、metadataForFormat: 这个方法的参数是一个用于定义元数据格式的NSString 对象, 它的返回值是一个包含全部相关元数据信息的NSArray
根据上面这三个方法,咱们看下面的Demo中的一个方法:
-(void)getAVMetadataItemMessage{ NSString * path = [[NSBundle mainBundle]pathForResource:@"薛之谦-像风同样.mp3" ofType:nil]; NSURL * url = [NSURL fileURLWithPath:path]; AVAsset * asset = [AVAsset assetWithURL:url]; NSArray * keys = @[@"availableMetadataFormats"]; NSMutableArray * metaArray =[NSMutableArray array]; // commonMetadata 从Common键空间获取元数据、这个属性会返回一个包括全部可用元数据的数组 NSArray * commonMetaArray = [asset commonMetadata]; NSLog(@"commonMetaArray = %@",commonMetaArray); // [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{ // availableMetadataFormats 这个属性会返回一个字符串 // 其中定义了资源中包含的全部元数据格式 for (NSString * format in asset.availableMetadataFormats) { // metadataForFormat 方法 这个方法包含一个用于定义元数据格式的NSString对象并返回一个包含全部先关元数据信息的NSArray [metaArray addObjectsFromArray:[asset metadataForFormat:format]]; } // NSLog(@"metaArray = %@",metaArray); // 使用 AVMetadataItem for (AVMetadataItem * item in metaArray) { NSLog(@"%@ : %@",item.key,item.value); } }]; }
上面的这段代码咱们须要注意的点在代码的注释中都已经提到了,下面咱们须要关心的是它的日志。
分析一下上面代码的日志:
commonMetadata 获取到的全部的可用的元数据的描述信息数组和经过availableMetadataFormats和metadataForFormat这两个组合方法获取到的元数据的描述信息是同样的。
还有一点和我在书中看的描述不一致的地方是 Key 和 Value 这两个属性的打印。按照书中的描述这样的写法获取到的 Key 是整型数据,而咱们获取到的是上面的输出,其实在最上面的描述信息中能够看到上面是有Key 这个属性的,这点暂时我也没明白,但事实是按照咱们上面的输出日志咱们的确是不能理解 TIT2 或者 TALA 甚至是 TPE1 这些Key表明的含义!其实他们都是MP3文件的标签,我上往搜了一下这些标签的含义,大体的说一下这些标签,方便之后使用时候查阅:
/* * TEXT: 歌词做者 TENC: 编码 WXXX: URL连接(URL) TCOP: 版权(Copyright) TOPE: 原艺术家 TCOM: 做曲家 TDAT: 日期 TPE3: 指挥者 TPE2: 乐队 TPE1: 艺术家至关于ID3v1的Artist TPE4: 翻译(记录员、修改员) TYER: 即ID3v1的Year USLT: 歌词 TSIZ: 大小 TALB: 专辑至关于ID3v1的Album TIT1: 内容组描述 TIT2: 标题至关于ID3v1的Title TIT3: 副标题 TCON: 流派(风格)至关于ID3v1的Genre AENC: 音频加密技术 TSSE: 编码使用的软件(硬件设置) TBPM: 每分钟节拍数 COMM: 注释至关于ID3v1的Comment TDLY: 播放列表返录 TRCK: 音轨(曲号)至关于ID3v1的Track TFLT: 文件类型 TIME: 时间 TKEY: 最初关键字 TLAN: 语言 TLEN: 长度 TMED: 媒体类型 TOAL: 原唱片集 TOFN: 原文件名 TOLY: 原歌词做者 TORY: 最初发行年份 TOWM: 文件全部者(许可证者) TPOS: 做品集部分 TPUB: 发行人 TRDA: 录制日期 TRSN: Intenet电台名称 TRSO: Intenet电台全部者 UFID: 惟一的文件标识符 TSRC: ISRC(国际的标准记录代码) */
上面的标签应该差很少包括了基本的标签,要是在之后的使用中有其余遇到的本身没有见过的再添加进来。
这一章最后说的竟然是 AVAssetExportSession
AVAssetExportSession 这个咱们再前面说过,在前面拍摄完视频以后咱们就利用这个 AVAssetExportSession 压缩视频。AVAssetExportSession 用于将AVAsset 内容根据导出预设条件进行转码,并将导出资源写到磁盘中,AVAssetExportSession 提供了多个功能来实现将一种格式转换为另外一个格式、修订资源的内容、修改资源的音频和视频行为,固然还有咱们最干星期的功能,即写入新的元数据。
使用AVAssetExportSession实例大体须要作下面这些:
一、须要一个AVAsset会话
二、根据前面的AVAsset会话实例以及设置的压缩质量初始化获得AVAssetExportSession对象
三、其实前面的里能够理解成导入设置,接下来就是导出设置,调出的地址outputURL以及outputFileType导出的格式
四、接下来就是利用exportAsynchronouslyWithCompletionHandler方法导出了,导出的数据会在改方法的Block中回调
五、最后就是在回调的block中根据AVAssetExportSession对象的status属性去判断压缩是否成功,进而进行本身想要的操做
上面的步骤大体上就说清楚了AVAssetExportSession,其余的API有兴趣能够进AVAssetExportSession的.h文件去看看,下面就是咱们前面有用到的一段源码:
#pragma mark -- #pragma mark -- 视频压缩方法 -(void)compressVideoWithFileUrl:(NSURL *)fileUrl{ /* 这里须要注意的一点就是在重复的路径上保存文件是不行的,能够选择在点击开始的时候删除以前的 也能够这样按照时间命名不一样的文件保存 在后面的 AVAssetWriter 也要注意这一点 */ // 压缩后的视频的方法命名 NSDateFormatter * formatter = [[NSDateFormatter alloc]init]; [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"]; // 压缩后的文件路径 self.videoPath = [NSString stringWithFormat:@"%@/%@.mov",NSTemporaryDirectory(),[formatter stringFromDate:[NSDate date]]]; // 先根据你传入的文件的路径穿件一个AVAsset AVAsset * asset = [AVAsset assetWithURL:fileUrl]; /* 根据urlAsset建立AVAssetExportSession压缩类 第二个参数的意义:经常使用 压缩中等质量 AVAssetExportPresetMediumQuality AVF_EXPORT NSString *const AVAssetExportPresetLowQuality NS_AVAILABLE_IOS(4_0); AVF_EXPORT NSString *const AVAssetExportPresetMediumQuality NS_AVAILABLE_IOS(4_0); AVF_EXPORT NSString *const AVAssetExportPresetHighestQuality NS_AVAILABLE_IOS(4_0); */ AVAssetExportSession * exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetMediumQuality]; // 优化压缩,这个属性能使压缩的质量更好 exportSession.shouldOptimizeForNetworkUse = YES; // 处处的文件的路径 exportSession.outputURL = [NSURL fileURLWithPath:self.videoPath]; // 导出的文件格式 /*! @constant AVFileTypeMPEG4 mp4格式的 AVFileTypeQuickTimeMovie mov格式的 @abstract A UTI for the MPEG-4 file format. @discussion The value of this UTI is @"public.mpeg-4". Files are identified with the .mp4 extension. 能够看看这个outputFileType格式,好比AVFileTypeMPEG4也能够写成public.mpeg-4,其余相似 */ exportSession.outputFileType = AVFileTypeQuickTimeMovie; NSLog(@"视频压缩后的presetName: %@",exportSession.presetName); // 压缩的方法 [exportSession exportAsynchronouslyWithCompletionHandler:^{ /* exportSession.status 枚举属性 typedef NS_ENUM(NSInteger, AVAssetExportSessionStatus) { AVAssetExportSessionStatusUnknown, AVAssetExportSessionStatusWaiting, AVAssetExportSessionStatusExporting, AVAssetExportSessionStatusCompleted, AVAssetExportSessionStatusFailed, AVAssetExportSessionStatusCancelled }; */ int exportStatus = exportSession.status; switch (exportStatus) { case AVAssetExportSessionStatusFailed: NSLog(@"压缩失败"); break; case AVAssetExportSessionStatusCompleted: { /* 压缩后的大小 也能够利用exportSession的progress属性,随时监测压缩的进度 */ NSData * data = [NSData dataWithContentsOfFile:self.videoPath]; float dataSize = (float)data.length/1024/1024; NSLog(@"视频压缩后大小 %f M", dataSize); } break; default: break; } }]; }
上面的内容大体就是书中第三章的内容了,具体的Demo能够在下面连接中下载!
个人博客即将同步至腾讯云+社区,邀请你们一同入驻。