这篇总结什么?


在该系列的上一篇的文章中,我们总结的大致内容如下:

1、视频录制  AVCaptureSession + AVCaptureMovieFileOutput

2、视频录制 AVCaptureSession + AVAssetWriter

3、AVCaptureSession + AVCaptureMovieFileOutput 与 AVCaptureSession + AVAssetWriter 的区别

这是这个系列总结文章的第三篇,前面我们提了音频以及视频的基本的播放,录制等等的知识,这篇文章我们总结开发秘籍中的第三章的内容 -- 资源和元数据。

说白了就是总结 AVAsset 这个类!

AVAsset


AVAsset是一个抽象类(抽象类中不一定包含抽象方法,但是包含抽象方法的类一定要被声明为抽象类。抽象类本身不具备实际的功能,只能用于派生其子类。抽象类中可以包含构造方法,但是构造方法不能被声明为抽象,简单点的说你不能实例化一个抽象类。然而,我们可以尝试复制该方案在Objective-C中采用一些技巧,要确保不能实例化你的父类),我们前面简单的说明了一下什么是抽象类,我们的AVAsset就是一个抽象类,你通过  assetWithURL 实际创建的就是他的子类,名为 AVURLAsset ,这一段话大家仔细理解一下。

一:AVAsset的异步载入  AVAsynchronousKeyValueLoading 协议

这个AVAsynchronousKeyValueLoading我们的AVAsset类是遵守了的,这个协议里面就两个必须实现的方法,我们解释一下这两个方法:

  1. /*
  2. typedef NS_ENUM(NSInteger, AVKeyValueStatus) {
  3.  
  4. AVKeyValueStatusUnknown,
  5. AVKeyValueStatusLoading,
  6. AVKeyValueStatusLoaded,
  7. AVKeyValueStatusFailed,
  8. AVKeyValueStatusCancelled
  9. };
  10.  
  11. // 这个方法可以用来查询给定属性的状态,如果返回的这个状态不是AVKeyValueStatusLoaded,那我们在此刻去请求这个状态的时候可能会出现卡顿
  12. - (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;
  13.  
  14. // keys参数就是我们要请求的属性数组,当完成请求之后就会在handler这个block回调给我们
  15. - (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
  16. */

我们简单的应用一下上面的知识,写个很简单的Demo,这个Demo还是会在我们这一系列文章的git上,我们请求一些我们本地数据的一些基本的属性,代码如下:

  1. -(void)getAssetMessage{
  2.  
  3. NSString * path = [[NSBundle mainBundle]pathForResource:@"薛之谦-像风一样.mp3" ofType:nil];
  4. NSURL * url = [NSURL fileURLWithPath:path];
  5.  
  6. AVAsset * asset = [AVAsset assetWithURL:url];
  7. NSArray * keys = @[@"duration"];
  8. [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
  9.  
  10. NSError * error;
  11. AVKeyValueStatus status = [asset statusOfValueForKey:@"duration" error:&error];
  12. switch (status) {
  13. case AVKeyValueStatusLoaded:
  14.  
  15. // 要更新UI的操作需要回到主线程
  16. NSLog(@"属性载入成功,你可以访问了");
  17. NSLog(@"duration = %.2f",CMTimeGetSeconds(asset.duration));
  18. break;
  19.  
  20. case AVKeyValueStatusLoading:
  21. NSLog(@"AVKeyValueStatusLoading");
  22. break;
  23.  
  24. case AVKeyValueStatusFailed:
  25. NSLog(@"AVKeyValueStatusFailed");
  26. break;
  27.  
  28. case AVKeyValueStatusUnknown:
  29. NSLog(@"AVKeyValueStatusUnknown");
  30. break;
  31. default:
  32. break;
  33. }
  34. }];
  35. }

上面的输出的日志如下:

需要注意的地方在代码注释中有些,经过上面的代码我们就异步的访问了它的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 的三个属性/方法:

1、commonMetadata 这个属性从Common键空间获取元数据,这个属性会返回以一个包含所有可用元数据的数组

2、availableMetadataFormats 这个属性会返回一个字符串数组,其中定义了资源中包含的所有的原数据格式

3、metadataForFormat: 这个方法的参数是一个用于定义元数据格式的NSString 对象, 它的返回值是一个包含所有相关元数据信息的NSArray

根据上面这三个方法,我们看下面的Demo中的一个方法:

  1. -(void)getAVMetadataItemMessage{
  2.  
  3. NSString * path = [[NSBundle mainBundle]pathForResource:@"薛之谦-像风一样.mp3" ofType:nil];
  4. NSURL * url = [NSURL fileURLWithPath:path];
  5.  
  6. AVAsset * asset = [AVAsset assetWithURL:url];
  7. NSArray * keys = @[@"availableMetadataFormats"];
  8. NSMutableArray * metaArray =[NSMutableArray array];
  9.  
  10. // commonMetadata 从Common键空间获取元数据、这个属性会返回一个包括所有可用元数据的数组
  11. NSArray * commonMetaArray = [asset commonMetadata];
  12. NSLog(@"commonMetaArray = %@",commonMetaArray);
  13.  
  14. //
  15. [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
  16.  
  17. // availableMetadataFormats 这个属性会返回一个字符串
  18. // 其中定义了资源中包含的所有元数据格式
  19. for (NSString * format in asset.availableMetadataFormats) {
  20.  
  21. // metadataForFormat 方法 这个方法包含一个用于定义元数据格式的NSString对象并返回一个包含所有先关元数据信息的NSArray
  22. [metaArray addObjectsFromArray:[asset metadataForFormat:format]];
  23. }
  24.  
  25. //
  26. NSLog(@"metaArray = %@",metaArray);
  27. // 使用 AVMetadataItem
  28. for (AVMetadataItem * item in metaArray) {
  29.  
  30. NSLog(@"%@ : %@",item.key,item.value);
  31. }
  32. }];
  33. }

上面的这段代码我们需要注意的点在代码的注释中都已经提到了,下面我们需要关心的是它的日志。

分析一下上面代码的日志:

commonMetadata 获取到的所有的可用的元数据的描述信息数组和通过availableMetadataFormats和metadataForFormat这两个组合方法获取到的元数据的描述信息是一样的。

还有一点和我在书中看的描述不一致的地方是 Key  和 Value 这两个属性的打印。按照书中的描述这样的写法获取到的 Key 是整型数据,而我们获取到的是上面的输出,其实在最上面的描述信息中可以看到上面是有Key 这个属性的,这点暂时我也没明白,但事实是按照我们上面的输出日志我们的确是不能理解 TIT2 或者 TALA 甚至是 TPE1 这些Key代表的含义!其实他们都是MP3文件的标签,我上往搜了一下这些标签的含义,大致的说一下这些标签,方便以后使用时候查阅:

  1. /*
  2. * TEXT: 歌词作者
  3. TENC: 编码
  4. WXXX: URL链接(URL)
  5. TCOP: 版权(Copyright)
  6. TOPE: 原艺术家
  7. TCOM: 作曲家
  8. TDAT: 日期
  9. TPE3: 指挥者
  10. TPE2: 乐队
  11. TPE1: 艺术家相当于ID3v1的Artist
  12. TPE4: 翻译(记录员、修改员)
  13. TYER: 即ID3v1的Year
  14. USLT: 歌词
  15. TSIZ: 大小
  16. TALB: 专辑相当于ID3v1的Album
  17. TIT1: 内容组描述
  18. TIT2: 标题相当于ID3v1的Title
  19. TIT3: 副标题
  20. TCON: 流派(风格)相当于ID3v1的Genre
  21. AENC: 音频加密技术
  22. TSSE: 编码使用的软件(硬件设置)
  23. TBPM: 每分钟节拍数 COMM: 注释相当于ID3v1的Comment
  24. TDLY: 播放列表返录
  25. TRCK: 音轨(曲号)相当于ID3v1的Track
  26. TFLT: 文件类型
  27. TIME: 时间
  28. TKEY: 最初关键字
  29. TLAN: 语言
  30. TLEN: 长度
  31. TMED: 媒体类型
  32. TOAL: 原唱片集
  33. TOFN: 原文件名
  34. TOLY: 原歌词作者
  35. TORY: 最初发行年份
  36. TOWM: 文件所有者(许可证者)
  37. TPOS: 作品集部分
  38. TPUB: 发行人
  39. TRDA: 录制日期
  40. TRSN: Intenet电台名称
  41. TRSO: Intenet电台所有者
  42. UFID: 唯一的文件标识符
  43. TSRC: ISRC(国际的标准记录代码)
  44.  
  45. */

上面的标签应该差不多包括了基本的标签,要是在以后的使用中有其他遇到的自己没有见过的再添加进来。

这一章最后说的居然是 AVAssetExportSession


AVAssetExportSession 这个我们再前面说过,在前面拍摄完视频之后我们就利用这个 AVAssetExportSession 压缩视频。AVAssetExportSession 用于将AVAsset 内容根据导出预设条件进行转码,并将导出资源写到磁盘中,AVAssetExportSession 提供了多个功能来实现将一种格式转换为另一个格式、修订资源的内容、修改资源的音频和视频行为,当然还有我们最干星期的功能,即写入新的元数据。

使用AVAssetExportSession实例大致需要做下面这些:

1、需要一个AVAsset会话

2、根据前面的AVAsset会话实例以及设置的压缩质量初始化得到AVAssetExportSession对象

3、其实前面的里可以理解成导入设置,接下来就是导出设置,调出的地址outputURL以及outputFileType导出的格式

4、接下来就是利用exportAsynchronouslyWithCompletionHandler方法导出了,导出的数据会在改方法的Block中回调

5、最后就是在回调的block中根据AVAssetExportSession对象的status属性去判断压缩是否成功,进而进行自己想要的操作

上面的步骤大致上就说清楚了AVAssetExportSession,其他的API有兴趣可以进AVAssetExportSession的.h文件去看看,下面就是我们前面有用到的一段源码:

  1. #pragma mark --
  2. #pragma mark -- 视频压缩方法
  3. -(void)compressVideoWithFileUrl:(NSURL *)fileUrl{
  4.  
  5. /*
  6.  
  7. 这里需要注意的一点就是在重复的路径上保存文件是不行的,可以选择在点击开始的时候删除之前的
  8. 也可以这样按照时间命名不同的文件保存
  9. 在后面的 AVAssetWriter 也要注意这一点
  10. */
  11. // 压缩后的视频的方法命名
  12. NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
  13. [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
  14.  
  15. // 压缩后的文件路径
  16. self.videoPath = [NSString stringWithFormat:@"%@/%@.mov",NSTemporaryDirectory(),[formatter stringFromDate:[NSDate date]]];
  17.  
  18. // 先根据你传入的文件的路径穿件一个AVAsset
  19. AVAsset * asset = [AVAsset assetWithURL:fileUrl];
  20. /*
  21.  
  22. 根据urlAsset创建AVAssetExportSession压缩类
  23. 第二个参数的意义:常用 压缩中等质量 AVAssetExportPresetMediumQuality
  24. AVF_EXPORT NSString *const AVAssetExportPresetLowQuality NS_AVAILABLE_IOS(4_0);
  25. AVF_EXPORT NSString *const AVAssetExportPresetMediumQuality NS_AVAILABLE_IOS(4_0);
  26. AVF_EXPORT NSString *const AVAssetExportPresetHighestQuality NS_AVAILABLE_IOS(4_0);
  27.  
  28. */
  29. AVAssetExportSession * exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
  30.  
  31. // 优化压缩,这个属性能使压缩的质量更好
  32. exportSession.shouldOptimizeForNetworkUse = YES;
  33. // 到处的文件的路径
  34. exportSession.outputURL = [NSURL fileURLWithPath:self.videoPath];
  35. // 导出的文件格式
  36. /*!
  37. @constant AVFileTypeMPEG4 mp4格式的 AVFileTypeQuickTimeMovie mov格式的
  38. @abstract A UTI for the MPEG-4 file format.
  39. @discussion
  40. The value of this UTI is @"public.mpeg-4".
  41. Files are identified with the .mp4 extension.
  42. 可以看看这个outputFileType格式,比如AVFileTypeMPEG4也可以写成public.mpeg-4,其他类似
  43. */
  44. exportSession.outputFileType = AVFileTypeQuickTimeMovie;
  45.  
  46. NSLog(@"视频压缩后的presetName: %@",exportSession.presetName);
  47. // 压缩的方法
  48. [exportSession exportAsynchronouslyWithCompletionHandler:^{
  49.  
  50. /*
  51. exportSession.status 枚举属性
  52. typedef NS_ENUM(NSInteger, AVAssetExportSessionStatus) {
  53. AVAssetExportSessionStatusUnknown,
  54. AVAssetExportSessionStatusWaiting,
  55. AVAssetExportSessionStatusExporting,
  56. AVAssetExportSessionStatusCompleted,
  57. AVAssetExportSessionStatusFailed,
  58. AVAssetExportSessionStatusCancelled
  59. };
  60. */
  61. int exportStatus = exportSession.status;
  62. switch (exportStatus) {
  63. case AVAssetExportSessionStatusFailed:
  64.  
  65. NSLog(@"压缩失败");
  66. break;
  67. case AVAssetExportSessionStatusCompleted:
  68. {
  69. /*
  70. 压缩后的大小
  71. 也可以利用exportSession的progress属性,随时监测压缩的进度
  72. */
  73. NSData * data = [NSData dataWithContentsOfFile:self.videoPath];
  74. float dataSize = (float)data.length/1024/1024;
  75. NSLog(@"视频压缩后大小 %f M", dataSize);
  76.  
  77. }
  78. break;
  79. default:
  80. break;
  81. }
  82. }];
  83. }

上面的内容大致就是书中第三章的内容了,具体的Demo可以在下面链接中下载!

Demo下载地址

我的博客即将同步至腾讯云+社区,邀请大家一同入驻。

AVFoundation 框架初探究(三)的更多相关文章

  1. AVFoundation 框架初探究(二)

    接着第一篇总结 系列第一篇地址:AVFoundation 框架初探究(一) 在第一篇的文章中,我们总结了主要有下面几个点的知识: 1.对AVFoundation框架整体的一个认识 2.AVSpeech ...

  2. AVFoundation 框架初探究(一)

    夜深时动笔 前面一篇文章写了视频播放的几种基本的方式,算是给这个系列开了一个头,这里面最想说和探究的就是AVFoundation框架,很想把这个框架不敢说是完全理解,但至少想把它弄明白它里面到底有什么 ...

  3. AVFoundation 框架初探究(四)

    叨叨两句 动手写这篇总结时候也是二月底过完年回来上班了,又开始新的一年了,今年会是什么样子?这问题可能得年底再回答自己了.在家窝了那么久,上班还是的接着看我们要看的东西,毕竟我们要做的事还真的太多的. ...

  4. 精通ASP.Net MVC 3 框架(第三版)学习笔记

    精通ASP.Net MVC 3 框架(第三版)学习笔记 代码才是王道. http://pan.baidu.com/s/1pJyL1cn

  5. spring框架学习(三)junit单元测试

    spring框架学习(三)junit单元测试 单元测试不是头一次听说了,但只是听说从来没有用过.一个模块怎么测试呢,是不是得专门为一单元写一个测试程序,然后将测试单元代码拿过来测试? 我是这么想的.学 ...

  6. Golang 网络爬虫框架gocolly/colly 三

    Golang 网络爬虫框架gocolly/colly 三 熟悉了<Golang 网络爬虫框架gocolly/colly一>和<Golang 网络爬虫框架gocolly/colly二& ...

  7. Spring框架的第三天

    ## Spring框架的第三天 ## ---------- **课程回顾:Spring框架第二天** 1. IOC的注解方式 * @Value * @Resource(name="" ...

  8. 自己写一个java的mvc框架吧(三)

    自己写一个mvc框架吧(三) 根据Method获取参数并转换参数类型 上一篇我们将url与Method的映射创建完毕,并成功的将映射关系创建起来了.这一篇我们将根据Method的入参参数名称.参数类型 ...

  9. 基于python的接口测试框架设计(三)接口测试的框架

    基于python的接口测试框架设计(三)接口测试的框架 其实我这里用到的是unittest单元测试框架,,这个框架好就好在比较清楚,,setup terdown都可以处理一些初始化及完成后的工作 主要 ...

随机推荐

  1. MVC(二)

    一: 在新接触MVC的时候可以先使用VS建一个MVC项目(不是空项目哟),MVC特别人性化的建一个示例,展示了MVC项目的基本组成.如下: App_Data 数据库文件,需根据数据库变动而变更. Ap ...

  2. 使用angularjs实现注册表单

    本文是在学习angularjs过程中做的相应的练习 github地址 https://github.com/2016Messi/angularjs1.6-form 演示地址 https://2016m ...

  3. 【python】字符串变量赋值时字符串可用单或双引号

    >>> name='萧峰' >>> print(name) 萧峰 >>> name="独孤求败" >>> p ...

  4. 人工智能技术实践篇:espeak开发环境调试

    一.前言 1.espeak版本: espeak-1.48.04-source 2.开发环境:VC+2015 二.正文 2.1 错误提示 LNK1104: cannot open file 'LIBC. ...

  5. 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解

    引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...

  6. C++ 头文件系列(set)

    简介 头文件包含set.multiset两个类模版,这里要描述的概念与map非常相似,甚至连成员函数都几乎一样,所以这篇随笔会很短. set set如果翻译成中文应该是集合的意思,这里更确切的说是唯一 ...

  7. 编译c语言程序扩展ruby

    环境: windows 10 64bit ruby 2.2.4p230 (2015-12-16 revision 53155) [i386-mingw32] gcc version 4.8.1 (GC ...

  8. Prism for WPF再探(基于Prism事件的模块间通信)

    上篇博文链接 Prism for WPF初探(构建简单的模块化开发框架) 一.简单介绍: 在上一篇博文中初步搭建了Prism框架的各个模块,但那只是搭建了一个空壳,里面的内容基本是空的,在这一篇我将实 ...

  9. Python个人项目--豆瓣图书个性化推荐

    项目名称: 豆瓣图书个性化推荐 需求简述:从给定的豆瓣用户名中,获取该用户所有豆瓣好友列表,从豆瓣好友中找出他们读过的且评分5星的图书,如果同一本书被不同的好友评5星,评分人数越多推荐度越高. 输入: ...

  10. Centos 7 防火墙firewalld命令

    今天自己在Hyper-v下搭建三台Linux服务器集群,用于学习ELKstack(即大数据日志解决技术栈Elasticsearch,Logstash,Kibana的简称),下载的Linux版本为cen ...