ASSets
AVFoundation框架的最核心的类是AVAsset,该类是整个AVFoundation框架设计的中心。AVAsset是一个抽象的(意味着你不能调用AVAsset的alloc或者new方法来创建一个AVAsset实例对象,而是通过该类的静态方法来创建实例对象)、不可变(比如NSArray也是个不可变的类)的类,该类包含了media的标题、时长、元数据、资源数据(比如视频的画面和音频的数据),因此,你需要了解该类提供的功能。
AVAsset把media抽象成两个层面:
- 是media格式的抽象层,即对于你来说,或者对于AVFoundation框架来说,不管是mp4格式的视频,还是MP3格式的音频,它都仅仅是一个资源(asset),这意味着你不需要关心“它”的实现细节(比如你不需要知道“它”的编码格式,“它”的视频容器类型),但是你也可以了解“它”的实现细节。此外,AVAsset还隐藏了media的存储位置(即media是从哪里获取的),即你不需要知道media是从网络获取的,还是从本地文件读取的。通过“隐藏”了media的格式和存储位置,AVAsset给你提供了一种简单且统一的方式来处理media。
- AVAsset不是media本身,而是media的容器。AVAsset包含了一个或者多个media轨道(track)和描述了media内容的元数据(metadata)。AVAssetTrack类既抽象(这里的抽象可以理解为代表)了media轨道(比如音频轨道或者视频轨道),也抽象了字幕、标题,所以AVAssetTrack是AVAsset里面的统一media类型。如下图所示,AVAsset和AVAssetTrack是一对多的关系。
你可以通过AVAsset提供的属性或者方法来获取media的时长、创建日期、音轨或者视轨等信息。
Media Metadata(元数据)
media文件(具体指视频文件或者音频文件)一般都会包含用于描述它们内容本身的元数据,比如标题、创作时间、作者等信息。AVFoundation框架提供的AVMetadataItem类用于描述一个个具体的元数据,比如视频标题通过一个AVMetadataItem类的实例对象来表示,作者也是通过一个AVMetadataItem类的实例对象来表示。
虽然每种media类型都有自己独特的编码格式(比如下一小节介绍的QuickTime格式、MPEG-4格式、MP3格式),但幸运的是AVFoundation框架对这些编码格式的细节都进行了抽象,以便让你用一种相对统一的方式来处理media元数据。接下来我们通过一个demo来了解AVFoundation框架如何获取视频文件(比如mp4文件)或者音频文件(比如mp3文件)的元数据的。
Media Metadata demo
如下录屏链接(demo来自《Learning AVFoundation》书中的第3章)所示,我们可以通过AVFoundation框架提供的“操作视频或者音频文件的元数据”的API来读取或者修改视频或者音频文件里面的元数据。
AVAsset类和AVMetadataItem类都属于AVFoundation框架。其中,AVAsset类是对media文件(比如某个mp4文件或者mp3文件)的“抽象”,也就是说,在ios或者mac的app里面,一个AVAsset实例对象就代表了某个具体的media文件,比如代表了A.mp4文件,此时你可以通过AVAsset的metadata属性来获取A.mp4文件的视频标题、文件的创建时间、文件的作者等信息。而AVMetadataItem类则用于描述一个个具体的元数据,比如视频标题通过一个AVMetadataItem类的实例对象来表示,“作者”也是通过一个AVMetadataItem类的实例对象来表示。AVAsset和AVMetadataItem的举例如下图所示。
Metadata formats(格式)
每种media类型都有自己独特的编码格式,常见的media格式有:QuickTime(文件名以.mov后缀结尾)、MPEG-4(mp4和m4v)、MPEG-4 audio(m4a)和MPEG-Layer 3 audio(mp3)。
QuickTime
QuickTime是苹果开发的一种跨平台的media架构,该架构的一部分就是QuickTime文件格式的规范说明。一个QuickTime文件由被称为atom的数据结构组成。一个atom一般包含media各种类型的数据或者其它atom。下图所示的是某个QuickTime文件内部的结构。
MPEG-4
MP4(也称MPEG-4)文件格式由QuickTime文件格式演化而来,这意味着MP4文件格式和QuickTime大体类似,MP4文件内部的结构也由atom组成。下图所示的是某个MP4文件内部的结构。
MP3
MP3文件一般包含了元数据(非必须)和被编码过的音频数据。现在的MP3文件大部分都通过ID3v2格式来存储描述关于音频的描述信息(比如作者名字)。下图所示的是某个MP3文件内部的结构,文件的开头包括文件的格式标识(下图中的文件格式是ID3)、version(版本号)、revision、flags。
AVPlayer
AVPlayer是AVFoundation框架用于播放视频的接口类,即如果要播放视频或,用它就对了。
- AVPlayer是一个被用来播放基于时间的media文件的控制器。该控制器并不是UIViewController,而是一个管理播放的普通对象。
- AVPlayer支持播放本地视频或者网络视频。
- AVPlayer实例对象支持复用。具体通过replaceCurrentItemWithPlayerItem:方法来切换数据源,进而达到复用播放器的目的。
下图所示的是当我们要播放一个视频时,所涉及的类。
AVPlayerLayer
AVPlayerLayer是CALayer的子类,所以AVPlayerLayer是一个可视化的组件,并且用于渲染视频内容到屏幕上,用法和CALayer相同(即可以作为UIView的backing layer,也可以直接添加到某个CALayer上)。
AVPlayerLayer有个很常见的属性:videoGravity。videoGravity属性指的是视频画面的拉伸效果,该属性的取值有3个,分别是AVLayerVideoGravityResizeAspect 、AVLayerVideoGravityResizeAspectFill 、AVLayerVideoGravityResize 。下面3张图所示的是,一个16:9的视频在一个4:3的矩形框中 在这3个取值的情况下的效果。
AVPlayerItem
背景:我们一般都是使用AVPlayer来播放AVAsset。如果你看苹果的AVAsset相关的API文档,你可以看到AVAsset的属性和方法都是用于获取media的数据,比如media的创建时间、时长,但是,你没法找到“获取当前播放器已经播放的时长”,也没法找到“seek方法”。那是因为AVAsset只抽象了一个media文件的静态特征,也就是说,AVAsset的属性都是不变的属性,不会随着media文件的播放而发生变化。但是,作为播放器(这里指AVPlayer)的使用方,你肯定需要知道当前视频的已经播放时长、需要seek操作等,于是,AVPlayerItem和AVPlayerItemTrack类就出现了。
AVPlayerItem类抽象类media文件的动态特征。该类提供了seekToTime:方法、currenTime属性等,以便你了解播放器在播放视频时的一些状态。
文章看到了这里,你就能明白为什么通过下面的代码段就可以播放视频。
播放器状态:AVPlayerItem的status属性(不包括播放结束的状态,因为没有播放结束的状态值!)
AVPlayerItem的status属性表示播放器的状态,如下图所示,我们可以通过KVO的方式来监听播放器状态的变化。
播放进度的监听
播放器进度的监听不是通过KVO的方式,而是通过AVPlayer的addPeriodicTimeObserverForInterval:queue:usingBlock:方法来实现。具体用法如下图demo代码(来自书的第4章)所示。
播放结束的监听(居然要通过NSNotification来监听。。)
AVPlayerItem的status属性虽然表示播放器的状态,但表示不全,仅有3个状态(状态如下图所示)。
播放结束时,我们可以通过监听AVPlayerItemDidPlayToEndTimeNotification来感知到,demo代码(来自书的第4章)如下图所示。
播放器的核心操作(由AVPlayer类的play、pause、seekToTime、stop方法提供)
如下图所示,AVPlayer提供了视频的播放、暂停、seek操作等方法。