IOS音视频(一)AVFoundation核心类

IOS音视频(一)AVFoundation核心类html

IOS音视频(二)AVFoundation视频捕捉ios

IOS音视频(三)AVFoundation 播放和录音算法

IOS音视频(四十三)AVFoundation 之 Audio Session编程

IOS音视频(四十四)AVFoundation 之 Audio Queue Servicesswift

IOS音视频(四十五)HTTPS 自签名证书 实现边下边播数组

IOS音视频(四十六)离线在线语音识别方案缓存

IOS音视频(一)AVFoundation核心类cookie

  • 作音视频开发是个很复杂的工做,须要咱们理解不少有关素材的知识:声学和视觉相关的科学理论,数的程序开发技术和有AVFoundation框架而引出的其余框架的知识,好比:Core Media, Core Video, Core Image, Core Audio, Media PlayerVideoToolbox 等等。网络

  • 要作IOS音视频相关的开发,确定要熟悉AVFoundation框架。学习框架最好的方式就是研究苹果官方文档:苹果官方AVFoundation框架介绍session

  • AVFoundation 是 Objective-C 中建立及编辑视听媒体文件的几个框架之一,其提供了检查、建立、编辑或从新编码媒体文件的接口,也使得从设备获取的视频实时数据可操纵。可是,一般状况,简单的播放或者录像,直接使用 AVKit 框架或者 UIImagePickerController 类便可。另外,值得注意的是,在 AVFoundation 框架中使用的基本数据结构,如时间相关的或描述媒体数据的数据结构都声明在 CoreMedia 框架中。

  • AVFoundation 是 OSX 系统和 iOS 系统中用于处理基于时间的媒体数据的高级 objectivec 框架,其设计过程高度依赖多线程机制,充分利用了多核硬件优点,大量使用 Block 和 GCD 机制。AVFoundation 能与高层级框架无缝衔接,也能提供低层级框架的功能和性能。

1. AVFoundation框架架构简介

  • 引用苹果官方文档介绍图以下:

iOS系统上的AVFoundation框架架构

Mac OS X系统上的AVFoundation框架架构

Core Audio是OS X和IOS系统上处理全部音频 事件的框架。Core Audio是有多高框架整合在一块儿的总称,为音频和MIDI内容的录制,播放和处理提供相应的接口。Core Audio也提供高级的接口,好比经过Audio Queue Services框架所提供的那些接口,主要处理基本的音频播放和录音相关功能。同时还会提供相对低层级的接口,尤为是Audio Units接口,它们提供了针对音频信号进行彻底控制的功能,并经过Audio Units让你可以构建一些复杂的音频处理模式,就像经过苹果公司的Logic Pro X和Avid's Pro Tolls工具所实现的功能同样。

Core Video是OS X 和IOS系统上针对数字视频所提供的管道模式。Core Video为其相对的Core Media提供图片缓存和缓存支持,提供了一个可以对数字视频逐帧访问的接口。该框架经过像素格式之间的转换并管理同步事项时的复杂的工做获得了有效简化。

Core Media 是AV Foundation所用到的低层级媒体管道的一部分。它提供针对音频样本和视频帧处理所需的低层级数据类型和接口。Core Media还提供了AV Foundation用的的基于CMTime数据类型的时间模型。CMTime及其相关数据类型通常在AV Foundation处理基于时间的操做时使用。

Core Animation时OS X和 iOS 提供的合成及动画相关框架。主要功能就是提供苹果平台所具备的美观,流畅的动画效果。提供了一个简单,声明行的编程模式,并已经封装了支持OpenGL 和OpenGL ES 功能的基于Object-C的各类类。使用Core Animation时,对于食品内容的播放和视频捕获这两个动做,AVFoundation 提供了硬件加速机制来对整个流程进行优化。AVFoundation 还能够利用Core Animation让开发者可以在视频编辑和播放过程当中添加动画标题和图片效果。

  • 苹果提供了AVFoundation框架,能够用来检测,编辑,建立,从新编码媒体文件,还能够实时获取设备的流媒体数据,实时操做这些被捕获的视频流数据。
  • 苹果推荐咱们尽量的使用高度抽象的接口:
  1. 若是只是简单播放视频文件,使用AVKit框架便可。
  2. 若是只是想简单录制视频,使用UIKit框架里的UIImagePickerController既能够实现。

1.1 AVFoundation框架

AVFoundation 提供的核心功能以下所示

AVFoundation框架1

  • AVFoundation 框架中最基本的类是 AVAsset ,它是一个或者多个媒体数据的集合,描述的是整个集合的属性,如标题、时长、大小等,而且没有特定的数据格式。集合的每个媒体数据都是统一的数据类型,称之为 track。简单的状况是一种数据是音频数据,一种是视频数据,而较复杂的状况是一种数据交织着音频和视频数据,而且 AVAsset 是可能有元数据的。 另外,须要明白的是在 AVFoundation 中,初始化了 asset 及 track 后,并不意味着资源已经可用,由于若资源自己并不携带自身信息时,那么系统须要本身计算相关信息,这个过程会阻塞线程,因此应该使用异步方式进行获取资源信息后的操做。

  • AVFoundation 中可使用 compositions 将多个媒体数据(video/audio tracks)合成为一个 asset ,这个过程当中,能够添加或移除 tracks ,调整它们的顺序,或者设置音频的音量和变化坡度,视频容量等属性。这些媒体数据的集合保存在内存中,直到使用 export session 将它导出到本地文件中。另外,还可使用 asset writer 建立 asset 。

  • 使用 capture session 协调从设备(如相机、麦克风)输入的数据和输出目标(如视频文件)。能够为 session 设置多个输入和输出,即便它正在工做,还能够经过它中止数据的流动。另外,还可使用 preview layer 将相机记录的影像实时展现给用户。

  • AVFoundation 中的回调处理并不保证回调任务在某个特定的线程或队列中执行,其遵循两个原则,UI 相关的操做在主线程中执行,其余回调须要为其指定调用的队列。

1.2 AVFoundation 之 Assets

Assets

  • 参考苹果官方介绍

  • AVAsset 是AVFoundation框架里的一个核心类,主要提供了一种形式独立的基于时间的视听数据抽象,例如电影文件或视频流。

    AVAsset类继承关系

  • AVAsset 包含须要一块儿呈现或处理的音轨集合,每一个音轨都是统一的媒体类型,包括(但不限于)音频、视频、文本、封闭字幕和字幕。asset对象提供关于整个资源的信息,好比它的持续时间或标题,以及表示的提示,好比它的大小。AVAsset 也能够有元数据,由AVMetadataItem的实例表示。

  • 跟踪由AVAssetTrack的实例表示,在一个典型的简单例子中,一个轨道表示音频组件,另外一个表示视频组件;在一个复杂的组合中,音频和视频可能会有多个重叠的音轨。

AVAssetTrack结构

  • 音轨具备许多属性,例如其类型(视频或音频)、可视和/或可听特征(视状况而定)、元数据和时间轴(以其父资产的形式表示)。磁道也有一组格式说明。数组包含CMFormatDescription对象(参见CMFormatDescriptionRef),每一个对象描述曲目引用的媒体样本的格式。包含统一媒体的磁道(例如,全部使用相同设置编码的磁道)将提供一个计数为1的数组。

  • 一个磁道自己能够被分割成段,用AVAssetTrackSegment的实例来表示。段是从源到资产跟踪时间线的时间映射。

  • CMTime是一个C结构,它将时间表示为一个有理数,具备一个分子(int64_t值)和一个分母(int32_t时间刻度)。从概念上讲,时间刻度指定了分子中每一个单位所占的秒数。所以,若是时间刻度是4,每一个单元表明四分之一秒;若是时间刻度是10,每一个单元表示十分之一秒,以此类推。您常用600的时间刻度,由于这是几种经常使用帧率的倍数:24帧用于电影,30帧用于NTSC(用于北美和日本的电视),25帧用于PAL(用于欧洲的电视)。使用600的时间刻度,您能够精确地表示这些系统中的任意数量的帧。除了简单的时间值以外,CMTime结构还能够表示非数值:+∞、-∞和不定。它还能够指示时间是否在某个点被四舍五入,并保持一个历元数。

  • 您可使用CMTimeMakeCMTimeMakeWithSeconds等相关函数之一建立时间(该函数容许您使用浮点值建立时间并指定首选时间刻度)。有几个函数用于基于时间的算术和比较时间,以下例所示:

CMTime time1 = CMTimeMake(200, 2); // 200 half-seconds
CMTime time2 = CMTimeMake(400, 4); // 400 quarter-seconds
 
// time1 and time2 both represent 100 seconds, but using different timescales.
if (CMTimeCompare(time1, time2) == 0) {
    NSLog(@"time1 and time2 are the same");
}
 
Float64 float64Seconds = 200.0 / 3;
CMTime time3 = CMTimeMakeWithSeconds(float64Seconds , 3); // 66.66... third-seconds
time3 = CMTimeMultiply(time3, 3);
// time3 now represents 200 seconds; next subtract time1 (100 seconds).
time3 = CMTimeSubtract(time3, time1);
CMTimeShow(time3);
 
if (CMTIME_COMPARE_INLINE(time2, ==, time3)) {
    NSLog(@"time2 and time3 are the same");
}


复制代码
  • AVFoundation 提供了多种方法来建立 asset ,能够简单的重编码已经存在的 asset ,这个过程可使用 export session 或者使用 asset reader 和 asset writer 。
  • 若要生成视频的缩略图,可使用 asset 初始化一个 AVAssetImageGenerator 实例对象,它会使用默承认用的视频 tracks 来生成图片。
  • 建立 AVAsset 或其子类 AVURLAsset 时,须要提供资源的位置,方法以下:
NSURL *url = <#视听资源的 URL ,能够是本地文件地址,也能够是网页媒体连接#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
复制代码
  • 上述方法的第二个参数是建立对象时的选择项,其中可能包含的选择项以下:

AVURLAssetPreferPreciseDurationAndTimingKey 是否须要资源的准确时长,及访问资源各个准确的时间点 AVURLAssetReferenceRestrictionsKey 连接其余资源的约束 AVURLAssetHTTPCookiesKey 添加资源可以访问的 HTTP cookies AVURLAssetAllowsCellularAccessKey 是否可以使用蜂窝网络

  • 建立并初始化一个 AVAsset 实例对象后,并不意味着该对象的全部属性均可以获取使用了,由于其中的一些属性须要额外的计算才可以获得,那么当获取这些属性时,可能会阻塞当前线程,因此须要异步获取这些属性。
  • AVAsset 与 AVAssetTrack 都遵循 AVAsynchronousKeyValueLoading 协议,这个协议中有如下两个方法:
//获取指定属性的状态
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;

//异步加载指定的属性集合
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
复制代码
  • 一般,咱们使用上述第二个方法异步加载想要的属性,然后在加载完成的回调 block 中使用第一个方法判断属性是否加载成功,而后访问想要的属性,执行本身的操做,以下代码:
NSURL *url = <#资源路径#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = @[@"duration",@"tracks"];

[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {

    NSError *error = nil;
    AVKeyValueStatus tracksStatus = [asset statusOfValueForKey:@"tracks" error:&error];
    //根据相应的属性状态进行对应的处理
    switch (tracksStatus) {
        case AVKeyValueStatusUnknown:
                //TODO
            break;
        case AVKeyValueStatusLoading:
                //TODO
            break;
        case AVKeyValueStatusLoaded:
                //TODO
            break;
        case AVKeyValueStatusFailed:
                //TODO
            break;
        case AVKeyValueStatusCancelled:
                //TODO
            break;
   }
}];
复制代码

1.3 AVFoundation 之 视频播放

AVFoundation Player1
AVFoundation Player2

1.3.1 AVPlayer

  • 具体能够参考苹果官方介绍的Playback章节

  • 要控制媒体资源的回放,可使用AVPlayer对象。在回放期间,您可使用AVPlayerItem实例来管理整个Asset的表示状态,使用AVPlayerItemTrack对象来管理单个曲目的表示状态。要显示视频,可使用AVPlayerLayer对象。

  • player是一个控制器对象,您可使用它来管理资产的回放,例如启动和中止回放,以及查找特定的时间。您使用AVPlayer实例来播放单个资产。您可使用AVQueuePlayer对象按顺序播放许多项(AVQueuePlayer是AVPlayer的子类)。在OS X上,你能够选择使用AVKit框架的AVPlayerView类来回放视图中的内容。

  • 播放器向您提供有关播放状态的信息,所以,若是须要,您能够将用户界面与播放器的状态同步。一般将播放器的输出定向到特定的核心动画层(AVPlayerLayerAVSynchronizedLayer的实例)。有关层的更多信息,请参见Core Animation Programming Guide

  • 尽管最终你想要播放资产,但你不能直接提供资产给AVPlayer 对象。而是提供AVPlayerItem的一个实例。播放器项管理与其相关联的资产的表示状态。播放器项包含播放器项跟踪—avplayeritemtrack的实例—对应于资产中的跟踪。各对象之间的关系如图所示:

    AVAsset对象关系

  • 这种抽象意味着您能够同时使用不一样的玩家来玩给定的资产,可是每一个玩家呈现的方式不一样。图2-2显示了一种可能性,即两个不一样的玩家使用不一样的设置来玩相同的资产。例如,使用项曲目,您能够在回放期间禁用特定的曲目(例如,您可能不想播放声音组件)。

    图2-2

  • 您可使用现有资产初始化播放器项,也能够直接从URL初始化播放器项,以便在特定位置播放资源(AVPlayerItem将随后为资源建立和配置资产)。不过,与AVAsset同样,简单地初始化播放器项并不必定意味着它能够当即播放。您能够观察(使用键值观察)一个项目的状态属性来肯定它是否准备好了,以及什么时候准备好了。

  • 使用一个 AVPlayer 类实例能够管理一个 asset 资源,可是它的属性 currentItem 才是 asset 的实际管理者。currentItem 是 AVPlayerItem 类的实例,而它的属性 tracks 包含着的 AVPlayerItemTracker 实例对应着 asset 中的各个 track 。

  • 那么,为了控制 asset 的播放,可使用 AVPlayer 类,在播放的过程当中,可使用 AVPlayerItem 实例管理整个 asset 的状态,使用 AVPlayerItemTracker 对象管理 asset 中每一个 track 的状态。另外,还可使用 AVPlayerLayer 类来显示播放的内容。

  • 因此,在建立 AVPlayer 实例对象时,除了能够直接传递资源文件的路径进行建立外,还能够传递 AVPlayerItem 的实例对象,以下方法:

+ (instancetype)playerWithURL:(NSURL *)URL;
+ (instancetype)playerWithPlayerItem:(nullable AVPlayerItem *)item;
- (instancetype)initWithURL:(NSURL *)URL;
- (instancetype)initWithPlayerItem:(nullable AVPlayerItem *)item;
复制代码
  • 建立后,并非能够直接使用,还要对它的状态进行检查,只有 status 的值为 AVPlayerStatusReadyToPlay 时,才能进行播放,因此这里须要使用 KVO 模式对该状态进行监控,以决定什么时候能够进行播放。
  • 若要管理多个资源的播放,则应使用 AVPlayer 的子类 AVQueuePlayer ,这个子类拥有的多个 AVPlayerItem 同各个资源相对应。

1.3.2 播放不一样类型的资源

  • 对于播放不一样类型的资源,须要进行的准备工做有所不一样,这主要取决于资源的来源。资源数据可能来自本地设备上文件的读取,也可能来自网络上数据流。
  • 对于本地文件,可使用文件地址建立 AVAsset 对象,然后使用该对象建立 AVPlayerItem 对象,最后将这个 item 对象与 AVPlayer 对象相关联。以后,即是等待 status 的状态变为 AVPlayerStatusReadyToPlay ,即可以进行播放了。
  • 对于网络数据的播放,不能使用地址建立 AVAsset 对象了,而是直接建立 AVPlayerItem 对象,将其同 AVPlayer 对象相关联,当 status 状态变为 AVPlayerStatusReadyToPlay 后,AVAssetAVAssetTrack 对象将由 item 对象建立。

1.3.3 播放控制

  • 经过调用 player 的 play 、pause 、setRate: 方法,能够控制 item 的播放,这些方法都会改变 player 的属性 rate 的值,该值为 1 表示 item 按正常速率播放,为 0 表示 item 暂停播放,0~1 表示低速播放,大于 1 表示高速播放,小于 0 表示从后向前播放。
  • item 的属性 timeControlStatus 的值表示当前 item 的状态,有下面 3 个值:
  1. AVPlayerTimeControlStatusPaused 暂停
  2. AVPlayerTimeControlStatusPlaying 播放
  3. AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate 等待按指定速率播放状态,该状态是当 rate 的值设置为非 0 值时,而 item 因某些缘由还没法播放的状况,而没法播放的缘由,可依经过 item 的 reasonForWaitingToPlay 属性值查看。
  • item 的属性 actionAtItemEnd 的值表示当前 item 播放结束后的动做,有下面 3 个值:
  1. AVPlayerActionAtItemEndAdvance 只适用于 AVQueuePlayer 类,表示播放队列中的下一个 item
  2. AVPlayerActionAtItemEndPause 表示暂停
  3. AVPlayerActionAtItemEndNone 表示无操做,当前 item 的 currentTime 属性值仍然按 rate 的值改变 item 的 currentTime 属性值表示当前 item 的播放时间,能够调用下面的方法指定 item 从何处进行播放。
//第二个方法可以进行更准确的跳转,可是须要进行额外的计算
- (void)seekToDate:(NSDate *)date;
- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter; (tolerance: 公差,先后公差)

//这两个方法传入了一个回调,当一个时间跳转请求被新的请求或其余操做打断时,回调也会被执行可是此时 finished 参数值为 NO
- (void)seekToTime:(CMTime)time completionHandler:(void (^)(BOOL finished))completionHandler NS_AVAILABLE(10_7, 5_0);
- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL finished))completionHandler NS_AVAILABLE(10_7, 5_0);
复制代码
  • 使用 AVQueuePlayer 管理多个 item 的播放,仍然能够经过调用 play 开始依次播放 item,调用 advanceToNextItem 方法播放下一个 item ,还能够经过下面的方法添加或移除 item 。
- (BOOL)canInsertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem;
- (void)insertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem;
- (void)removeItem:(AVPlayerItem *)item;
- (void)removeAllItems;
复制代码
  • 可使用下面的方法监听播放时间的变化,须要强引用这两个方法返回的监听者。
- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;
- (id)addBoundaryTimeObserverForTimes:(NSArray<NSValue *> *)times queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(void))block;
复制代码
  • 用上面的方法每注册一个监听者,就须要对应的使用下面的方法进行注销,而且在注销以前,要确保没有 block 被执行。- (void)removeTimeObserver:(id)observer;
  • 当 item 播放结束后,再次调用 player 的方法 play 不会使 item 从新播放,要实现重播,能够注册一个 AVPlayerItemDidPlayToEndTimeNotification 通知,当接收到这个通知时,能够调 seekToTime: 方法,传入 kCMTimeZero 参数,将 player 的播放时间重置。

1.3.4 自定义播放--音频

  • 要在媒体资源播放的过程当中实现音频的自定义播放,须要用 AVMutableAudioMix 对不一样的音频进行编辑。这个类的实例对象的属性 inputParameters 是音量描述对象的集合,每一个对象都是对一个 audio track 的音量变化的描述,以下:
AVMutableAudioMix *mutableAudioMix = [AVMutableAudioMix audioMix];

AVMutableAudioMixInputParameters *mixParameters1 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTrack1];
[mixParameters1 setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero, mutableComposition.duration/2)];
[mixParameters1 setVolumeRampFromStartVolume:0.f toEndVolume:1.f timeRange:CMTimeRangeMake(mutableComposition.duration/2, mutableComposition.duration)];

AVMutableAudioMixInputParameters *mixParameters2 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTrack2];
[mixParameters2 setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero, mutableComposition.duration)];

mutableAudioMix.inputParameters = @[mixParameters1, mixParameters2];
复制代码

1.3.4.1 AVAudioMix

  • 该类中有一个属性 inputParameters ,它是 AVAudioMixInputParameters 实例对象的集合,每一个实例都是对音频播放方式的描述。可见,AVAudioMix 并不直接改变音频播放的方式,其只是存储了音频播放的方式。

1.3.4.2 AVMutableAudioMix

  • AVMutableAudioMixAVAudioMix 的子类,它的方法 audioMix 返回一个 inputParameters 属性为空的实例。

1.3.4.3 AVAudioMixInputParameters

  • 这个类是音量变化的描述类,它同一个音频的 track 相关联,并设置音量随时间变化的算法,其获取音量变化的方法以下:
//获取的音量变化范围 timeRange 应包含指定的时刻 time 不然最终返回 NO
//startVolume 获取音量开始变化时的初始音量
//endVolume 获取音量变化结束时的音量
//timeRang 是实际音量变化的范围,它应该包含指定的 time
- (BOOL)getVolumeRampForTime:(CMTime)time startVolume:(nullable float *)startVolume endVolume:(nullable float *)endVolume timeRange:(nullable CMTimeRange *)timeRange;
复制代码

1.3.4.4 AVMutableAudioMixInputParameters

  • AVMutableAudioMixInputParameters 是 AVAudioMixInputParameters 的子类,它提供了直接设置某个时刻或时间段的音量的方法。
//根据提供的 track 建立一个实例,此时的音量描述数据为空
+ (instancetype)audioMixInputParametersWithTrack:(nullable AVAssetTrack *)track;

//建立一个实例,此时的音量变化描述是空的,且 trackID 为 kCMPersistentTrackID_Invalid
+ (instancetype)audioMixInputParameters;

//设置某个时间范围内的初始音量及结束音量
- (void)setVolumeRampFromStartVolume:(float)startVolume toEndVolume:(float)endVolume timeRange:(CMTimeRange)timeRange;

//设置某个时刻的音量
- (void)setVolume:(float)volume atTime:(CMTime)time;
复制代码

1.3.5 自定义播放--视频

  • 同音频的自定义播放同样,要实现视频的自定义播放,仅仅将视频资源集合到一块儿是不够的,须要使用 AVMutableVideoComposition 类来定义不一样的视频资源在不一样的时间范围内的播放方式。

1.3.5.1 AVVideoComposition

  • AVVideoComposition 是 AVMutableVideoComposition 的父类,它的主要属性和方法以下:
//该类的构造类,提供自定义的构造类时,提供的类要遵照 AVVideoCompositing 协议
@property (nonatomic, readonly, nullable) Class<AVVideoCompositing> customVideoCompositorClass NS_AVAILABLE(10_9, 7_0);

//视频每一帧的刷新时间
@property (nonatomic, readonly) CMTime frameDuration;

//视频显示时的大小范围
@property (nonatomic, readonly) CGSize renderSize;

//视频显示范围大小的缩放比例(仅仅对 iOS 有效)
@property (nonatomic, readonly) float renderScale;

//描述视频集合中具体视频播放方式信息的集合,其是遵循 AVVideoCompositionInstruction 协议的类实例对象
//这些视频播放信息构成一个完整的时间线,不能重叠,不能间断,而且在数组中的顺序即为相应视频的播放顺序
@property (nonatomic, readonly, copy) NSArray<id <AVVideoCompositionInstruction>> *instructions;

//用于组合视频帧与动态图层的 Core Animation 的工具对象,能够为 nil 
@property (nonatomic, readonly, retain, nullable) AVVideoCompositionCoreAnimationTool *animationTool;

//直接使用一个 asset 建立一个实例,建立的实例的各个属性会根据 asset 中的全部的 video tracks 的属性进行计算并适配,因此在调用该方法以前,确保 asset 中的属性已经加载
//返回的实例对象的属性 instructions 中的对象会对应每一个 asset 中的 track 中属性要求
//返回的实例对象的属性 frameDuration 的值是 asset 中 全部 track 的 nominalFrameRate 属性值最大的,若是这些值都为 0 ,默认为 30fps
//返回的实例对象的属性 renderSize 的值是 asset 的 naturalSize 属性值,若是 asset 是 AVComposition 类的实例。不然,renderSize 的值将包含每一个 track 的 naturalSize 属性值
+ (AVVideoComposition *)videoCompositionWithPropertiesOfAsset:(AVAsset *)asset NS_AVAILABLE(10_9, 6_0);

//这三个属性设置了渲染帧时的颜色空间、矩阵、颜色转换函数,可能的值都在 AVVideoSetting.h 文件中定义
@property (nonatomic, readonly, nullable) NSString *colorPrimaries NS_AVAILABLE(10_12, 10_0);
@property (nonatomic, readonly, nullable) NSString *colorYCbCrMatrix NS_AVAILABLE(10_12, 10_0);
@property (nonatomic, readonly, nullable) NSString *colorTransferFunction NS_AVAILABLE(10_12, 10_0);

//该方法返回一个实例,它指定的 block 会对 asset 中每个有效的 track 的每一帧进行渲染获得 CIImage 实例对象
//在 block 中进行每一帧的渲染,成功后应调用 request 的方法 finishWithImage:context: 并将获得的 CIImage 对象做为参数
//如果渲染失败,则应调用 finishWithError: 方法并传递错误信息

+ (AVVideoComposition *)videoCompositionWithAsset:(AVAsset *)asset
             applyingCIFiltersWithHandler:(void (^)(AVAsynchronousCIImageFilteringRequest *request))applier NS_AVAILABLE(10_11, 9_0);
复制代码

1.3.5.2 AVMutableVideoComposition

  • AVMutableVideoComposition 是 AVVideoComposition 的可变子类,它继承父类的属性能够改变,而且新增了下面的建立方法。
//这个方法建立的实例对象的属性的值都是 nil 或 0,可是它的属性都是能够进行修改的
+ (AVMutableVideoComposition *)videoComposition;
复制代码

1.3.5.3 AVVideoCompositionInstruction

  • 在上述的两个类中,真正包含有视频播放方式信息的是 instructions 属性,这个集合中的对象都遵循 AVVideoCompositionInstruction 协议,若不使用自定义的类,那么可使用 AVFoundation 框架中的 AVVideoCompositionInstruction 类。
  • 该类的相关属性以下:
//表示该 instruction 生效的时间范围
@property (nonatomic, readonly) CMTimeRange timeRange;

//指定当前时间段的 composition 的背景色
//若是没有指定,那么使用默认的黑色
//若是渲染的像素没有透明度通道,那么这个颜色也会忽略透明度
@property (nonatomic, readonly, retain, nullable) __attribute__((NSObject)) CGColorRef backgroundColor;

//AVVideoCompositionLayerInstruction 类实例对象的集合,描述各个视频资源帧的层级及组合关系
//按这个数组的顺序,第一个显示在第一层,第二个在第一层下面显示,以此类推
@property (nonatomic, readonly, copy) NSArray<AVVideoCompositionLayerInstruction *> *layerInstructions;

//代表该时间段的视频帧是否须要后期处理
//若为 NO,后期图层的处理将跳过该时间段,这样可以提升效率
//为 YES 则按默认操做处理(参考 AVVideoCompositionCoreAnimationTool 类)
@property (nonatomic, readonly) BOOL enablePostProcessing;

//当前 instruction 中须要进行帧组合的全部的 track ID 的集合,由属性 layerInstructions 计算获得
@property (nonatomic, readonly) NSArray<NSValue *> *requiredSourceTrackIDs NS_AVAILABLE(10_9, 7_0);

//若是当前的 instruction 在该时间段内的视频帧组合后,实质获得的是某个源视频的帧,那么就返回这个视频资源的 ID
@property (nonatomic, readonly) CMPersistentTrackID passthroughTrackID NS_AVAILABLE(10_9, 7_0); 
复制代码

1.3.5.4 AVMutableVideoCompositionInstruction

  • AVMutableVideoCompositionInstruction 是 AVVideoCompositionInstruction 的子类,其继承的父类的属性可进行修改,而且提供了建立属性值为 nil 或无效的实例的方法。
+ (instancetype)videoCompositionInstruction;
复制代码

1.3.5.5 AVVideoCompositionLayerInstruction

  • AVVideoCompositionLayerInstruction 是对给定的视频资源的不一样播放方式进行描述的类,经过下面的方法,能够获取仿射变化、透明度变化、裁剪区域变化的梯度信息。
//获取包含指定时间的仿射变化梯度信息
//startTransform、endTransform 用来接收变化过程的起始值与结束值
//timeRange 用来接收变化的持续时间范围
//返回值表示指定的时间 time 是否在变化时间 timeRange 内
- (BOOL)getTransformRampForTime:(CMTime)time startTransform:(nullable CGAffineTransform *)startTransform endTransform:(nullable CGAffineTransform *)endTransform timeRange:(nullable CMTimeRange *)timeRange;

//获取包含指定时间的透明度变化梯度信息
//startOpacity、endOpacity 用来接收透明度变化过程的起始值与结束值
//timeRange 用来接收变化的持续时间范围
//返回值表示指定的时间 time 是否在变化时间 timeRange 内
- (BOOL)getOpacityRampForTime:(CMTime)time startOpacity:(nullable float *)startOpacity endOpacity:(nullable float *)endOpacity timeRange:(nullable CMTimeRange *)timeRange;

//获取包含指定时间的裁剪区域的变化梯度信息
//startCropRectangle、endCropRectangle 用来接收变化过程的起始值与结束值
//timeRange 用来接收变化的持续时间范围
//返回值表示指定的时间 time 是否在变化时间 timeRange 内
- (BOOL)getCropRectangleRampForTime:(CMTime)time startCropRectangle:(nullable CGRect *)startCropRectangle endCropRectangle:(nullable CGRect *)endCropRectangle timeRange:(nullable CMTimeRange *)timeRange NS_AVAILABLE(10_9, 7_0);
复制代码

1.3.5.6 AVMutableVideoCompositionLayerInstruction

  • AVMutableVideoCompositionLayerInstruction 是 AVVideoCompositionLayerInstruction 的子类,它能够改变 composition 中的 track 资源播放时的仿射变化、裁剪区域、透明度等信息。

  • 相比于父类,该子类还提供了建立实例的方法:

//这两个方法的区别在于,前者返回的实例对象的属性 trackID 的值是 track 的 trackID 值
//而第二个方法的返回的实例对象的属性 trackID 的值为 kCMPersistentTrackID_Invalid
+ (instancetype)videoCompositionLayerInstructionWithAssetTrack:(AVAssetTrack *)track;
+ (instancetype)videoCompositionLayerInstruction;

复制代码
  • 该类的属性表示 instruction 所做用的 track 的 ID:
@property (nonatomic, assign) CMPersistentTrackID trackID;
复制代码
  • 设置了 trackID 后,经过下面的方法,进行剃度信息的设置:
//设置视频中帧的仿射变化信息
//指定了变化的时间范围、起始值和结束值,其中坐标系的原点为左上角,向下向右为正方向
- (void)setTransformRampFromStartTransform:(CGAffineTransform)startTransform toEndTransform:(CGAffineTransform)endTransform timeRange:(CMTimeRange)timeRange;

//设置 instruction 的 timeRange 范围内指定时间的仿射变换,该值会一直保持,直到被再次设置
- (void)setTransform:(CGAffineTransform)transform atTime:(CMTime)time;

//设置透明度的梯度信息,提供的透明度初始值和结束值应在0~1之间
//变化的过程是线形的
- (void)setOpacityRampFromStartOpacity:(float)startOpacity toEndOpacity:(float)endOpacity timeRange:(CMTimeRange)timeRange;

//设置指定时间的透明度,该透明度会一直持续到下一个值被设置
- (void)setOpacity:(float)opacity atTime:(CMTime)time;

//设置裁剪矩形的变化信息
- (void)setCropRectangleRampFromStartCropRectangle:(CGRect)startCropRectangle toEndCropRectangle:(CGRect)endCropRectangle timeRange:(CMTimeRange)timeRange NS_AVAILABLE(10_9, 7_0);

//设置指定时间的裁剪矩形
- (void)setCropRectangle:(CGRect)cropRectangle atTime:(CMTime)time NS_AVAILABLE(10_9, 7_0);
复制代码

1.3.5.7 AVVideoCompositionCoreAnimationTool

  • 在自定义视频播放时,可能须要添加水印、标题或者其余的动画效果,须要使用该类。该类一般用来协调离线视频中图层与动画图层的组合(如使用 AVAssetExportSessionAVAssetReaderAVAssetReader 类导出视频文件或读取视频文件时),而如果在线实时的视频播放,应使用 AVSynchronizedLayer 类来同步视频的播放与动画的效果。

  • 在使用该类时,注意动画在整个视频的时间线上都可以被修改,因此,动画的开始时间应该设置为 AVCoreAnimationBeginTimeAtZero ,这个值其实比 0 大,属性值 removedOnCompletion 应该置为 NO,以防当动画执行结束后被移除,而且不该使用与任何的 UIView 相关联的图层。

  • 做为视频组合的后期处理工具类,主要方法以下:

//向视频组合中添加一个动画图层,这个图层不能在任何图层树中
//提供的参数 trackID 应由方法 [AVAsset unusedTrackID] 获得,它不与任何视频资源的 trackID 相关
//AVVideoCompositionInstruction 的属性 layerInstructions 包含的 AVVideoCompositionLayerInstruction 实例对象中应该有
//该 trackID 一致的 AVVideoCompositionLayerInstruction 实例对象,而且为性能考虑,不该使用该对象设置 transform 的变化
//在 iOS 中,CALayer 做为 UIView 的背景图层,其内容的是否可以翻转,由方法 contentsAreFlipped 决定(若是全部的图层包括子图层,该方法返回的值为 YES 的个数为奇数个,表示能够图层中内容能够垂直翻转)
//因此这里的 layer 若用来设置 UIView 的 layer 属性,或做为其中的子图层,其属性值 geometryFlipped 应设置为 YES ,这样则可以保持是否可以翻转的结果一致
+ (instancetype)videoCompositionCoreAnimationToolWithAdditionalLayer:(CALayer *)layer asTrackID:(CMPersistentTrackID)trackID;

//将放在图层 videoLayer 中的组合视频帧同动画图层 animationLayer 中的内容一块儿进行渲染,获得最终的视频帧
//一般,videoLayer 是 animationLayer 的子图层,而 animationLayer 则不在任何图层树中
+ (instancetype)videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:(CALayer *)videoLayer inLayer:(CALayer *)animationLayer;

//复制 videoLayers 中的每个图层,与 animationLayer一块儿渲染获得最中的帧
////一般,videoLayers 中的图层都在 animationLayer 的图层树中,而 animationLayer 则不属于任何图层树
+ (instancetype)videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayers:(NSArray<CALayer *> *)videoLayers inLayer:(CALayer *)animationLayer NS_AVAILABLE(10_9, 7_0);
复制代码

1.3.5.8 AVVideoCompositionValidationHandling

  • 当咱们通过编辑后获得一个视频资源 asset ,而且为该资源设置了自定义播放信息 video composition ,须要验证对于这个 asset 而言,video composition 是否有效,能够调用 AVVideoComposition 的校验方法。
/* @param asset 设置第一个参数的校验内容,设置 nil 忽略这些校验 1. 该方法能够校验 AVVideoComposition 的属性 instructions 是否符合要求 2. 校验 instructions 中的每一个 AVVideoCompositionInstruction 对象的 layerInstructions 属性中的 每个 AVVideoCompositionLayerInstruction 对象 trackID 值是否对应 asset 中 track 的 ID 或 AVVideoComposition 的 animationTool 实例 3. 校验时间 asset 的时长是否与 instructions 中的时间范围相悖 @param timeRange 设置第二个参数的校验内容 1. 校验 instructions 的全部的时间范围是否在提供的 timeRange 的范围内, 若要忽略该校验,能够传参数 CMTimeRangeMake(kCMTimeZero, kCMTimePositiveInfinity) @param validationDelegate 设置遵循 AVVideoCompositionValidationHandling 协议的代理类,用来处理校验过程当中的报错,能够为 nil */
- (BOOL)isValidForAsset:(nullable AVAsset *)asset timeRange:(CMTimeRange)timeRange validationDelegate:(nullable id<AVVideoCompositionValidationHandling>)validationDelegate NS_AVAILABLE(10_8, 5_0);
复制代码
  • 设置的代理对象要遵循协议 AVVideoCompositionValidationHandling ,该对象在实现下面的协议方法时,若修改了传递的 composition 参数,上面的校验方法则会抛出异常。

  • 该协议提供了如下回调方法,全部方法的返回值用来肯定是否继续进行校验以获取更多的错误。

//报告 videoComposition 中有无效的值
- (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingInvalidValueForKey:(NSString *)key NS_AVAILABLE(10_8, 5_0);

//报告 videoComposition 中有时间段没有相对应的 instruction
- (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingEmptyTimeRange:(CMTimeRange)timeRange NS_AVAILABLE(10_8, 5_0);

//报告 videoComposition 中的 instructions 中 timeRange 无效的实例对象
//多是 timeRange 自己为 CMTIMERANGE_IS_INVALID 
//或者是该时间段同上一个的 instruction 的 timeRange 重叠
//也多是其开始时间比上一个的 instruction 的 timeRange 的开始时间要早
- (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingInvalidTimeRangeInInstruction:(id<AVVideoCompositionInstruction>)videoCompositionInstruction NS_AVAILABLE(10_8, 5_0);

//报告 videoComposition 中的 layer instruction 同调用校验方法时指定的 asset 中 track 的 trackID 不一致
//也不与 composition 使用的 animationTool 的trackID 一致
- (BOOL)videoComposition:(AVVideoComposition *)videoComposition shouldContinueValidatingAfterFindingInvalidTrackIDInInstruction:(id<AVVideoCompositionInstruction>)videoCompositionInstruction layerInstruction:(AVVideoCompositionLayerInstruction *)layerInstruction asset:(AVAsset *)asset NS_AVAILABLE(10_8, 5_0);
复制代码

1.3.5.9 AVVideoCompositionValidationHandling

1.4 AVFoundation 之 视音频编辑

AVFoundation 编辑视频1

AVFoundation 编辑视2

  • 详情能够参考苹果官方文档:Editing章节
  • AVFoundation使用组合从现有的媒体片断(一般是一个或多个视频和音频轨道)建立新资产。您可使用可变组合来添加和删除轨迹,并调整它们的时间顺序。你也能够设置音轨的相对音量和倾斜;设置视频轨迹的不透明度和不透明度坡道。合成是存储在内存中的媒体片断的集合。当您使用导出会话导出一个组合时,它会折叠成一个文件。您还可使用资产写入器从媒体(例如示例缓冲区或静态图像)建立资产。
  • AVFoundation 框架中提供了丰富的接口用于视听资源的编辑,其中的关键是 composition ,它将不一样的 asset 相结合并造成一个新的 asset 。使用 AVMutableComposition 类能够增删 asset 来将指定的 asset 集合到一块儿。除此以外,若想将集合到一块儿的视听资源以自定义的方式进行播放,须要使用 AVMutableAudioMixAVMutableVideoComposition类对其中的资源进行协调管理。最终要使用 AVAssetExportSession 类将编辑的内容保存到文件中。
  • AVFoundation框架提供了一组功能丰富的类,以方便编辑视听资产。AVFoundation编辑API的核心是复合。组合就是来自一个或多个不一样媒体资产的音轨集合。AVMutableComposition类提供了一个接口,用于插入和删除轨迹,以及管理它们的时间顺序。图3-1显示了如何将现有资产组合拼凑成新资产。若是您想作的只是将多个资产按顺序合并到一个文件中,那么这就是您所须要的所有细节。若是你想在你的做曲中对音轨进行任何自定义音频或视频处理,你须要分别合并一个音频混合或一个视频合成。

将现有资产组合拼凑成新资产

  • 使用AVMutableAudioMix类,您能够在组合中的音频轨道上执行自定义音频处理,如图3-2所示。目前,您能够为音轨指定最大音量或设置音量斜坡。

    音频混合

  • 您可使用AVMutableVideoComposition类来直接编辑合成中的视频轨迹,如图3-3所示。对于单个视频合成,您能够为输出视频指定所需的渲染大小和比例以及帧持续时间。经过视频合成的指令(由AVMutableVideoCompositionInstruction类表示),您能够修改视频的背景颜色并应用层指令。这些层指令(由AVMutableVideoCompositionLayerInstruction类表示)可用于对组合中的视频轨道应用转换、转换坡道、不透明度和不透明度坡道。video composition类还容许您使用animationTool属性将核心动画框架的效果引入到视频中。

视频合成

  • 要将组合与音频和视频组合组合在一块儿,可使用AVAssetExportSession对象,如图3-4所示。使用组合初始化导出会话,而后分别将音频混合和视频组合分配给audioMixvideoComposition属性。

音视频组合

1.4.1 AVAssetExportSession

  • 使用 AVAssetExportSession 类对视频进行裁剪及转码,即将一个 AVAsset 类实例修改后保存为另外一个 AVAsset 类实例,最后保存到文件中。
  • 在修改资源以前,为避免不兼容带来的错误,能够先调用下面的方法,检查预设置是否合理。
//获取与 asset 兼容的预设置
+ (NSArray<NSString *> *)exportPresetsCompatibleWithAsset:(AVAsset *)asset;

//判断提供的预设置和输出的文件类型是否与 asset 相兼容
+ (void)determineCompatibilityOfExportPreset:(NSString *)presetName withAsset:(AVAsset *)asset outputFileType:(nullable NSString *)outputFileType completionHandler:(void (^)(BOOL compatible))handler NS_AVAILABLE(10_9, 6_0);
复制代码
  • 除了设置文件类型外,还能够设置文件的大小、时长、范围等属性,一切准备就绪后,调用方法:- (void)exportAsynchronouslyWithCompletionHandler:(void (^)(void))handler;

  • 进行文件的导出,导出结束后,会调用 handler 回调,在回调中应该检查 AVAssetExportSession 的 status 属性查看导出是否成功,若指定的文件保存地址在沙盒外,或在导出的过程当中有电话打入都会致使文件保存失败,以下:

- (void)exportVideo:(NSURL *)url {
    AVAsset *anAsset = [AVAsset assetWithURL:url];

    [AVAssetExportSession determineCompatibilityOfExportPreset:AVAssetExportPresetHighestQuality
                                                     withAsset:anAsset
                                                outputFileType:AVFileTypeMPEG4
                                             completionHandler:^(BOOL compatible) {
        if (compatible){
            AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:anAsset
                                                                                   presetName:AVAssetExportPresetHighestQuality];

            exportSession.outputFileType = AVFileTypeMPEG4;

            CMTime start = CMTimeMakeWithSeconds(1.0, 600);
            CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
            CMTimeRange range = CMTimeRangeMake(start, duration);
            exportSession.timeRange = range;
            [exportSession exportAsynchronouslyWithCompletionHandler:^{

                switch ([exportSession status]) {
                    case AVAssetExportSessionStatusCompleted:
                        NSLog(@"completed");
                        break;
                    case AVAssetExportSessionStatusFailed:
                        NSLog(@"failed");
                        break;
                    case AVAssetExportSessionStatusCancelled:
                        NSLog(@"canceled");
                        break;
                    default:
                        break;
                }
            }];
        }
    }];
}
复制代码

1.4.2 AVComposition

  • AVAsset 拥有多个 AVAssetTrack 同样,做为子类的 AVComposition 也拥有多个 AVCompositionTrack ,而 AVCompositionTrackAVAssetTrack 的子类。因此,AVComposition 实例对象是多个 track 的集合,真正描述媒体属性的是 AVCompositionTrack 实例对象。而 AVCompositionTrack 又是媒体数据片断的集合,这些数据片断由 AVCompositionTrackSegment 类进行描述。

  • 该类的相关属性和方法以下:

//获取 composition 中包含的 tracks
@property (nonatomic, readonly) NSArray<AVCompositionTrack *> *tracks;

//获取 composition 中可视媒体资源播放时在屏幕上显示的大小
@property (nonatomic, readonly) CGSize naturalSize;

//获取 composition 生成 asset 时的指定配置
@property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *URLAssetInitializationOptions NS_AVAILABLE(10_11, 9_0);

//根据不一样的参数,获取 composition 中的 track
- (nullable AVCompositionTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;
- (NSArray<AVCompositionTrack *> *)tracksWithMediaType:(NSString *)mediaType;
- (NSArray<AVCompositionTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;
复制代码
  • 值得注意的是 AVComposition 类中并无提供初始化方法,通常咱们使用它的子类 AVMutableComposition ,进行各类操做后,再生成 AVComposition 实例以供查询,以下:
AVMutableComposition *mutableComposition = [AVMutableComposition composition];

//进行添加资源等操做
<#····#>

//使用可变的 composition 生成一个不可变的 composition 以供使用
AVComposition *composition = [myMutableComposition copy];
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:composition];
复制代码

1.4.3 AVMutableComposition

  • AVMutableCompositionAVComposition 的子类,其包含的 tracks 则是 AVCompositionTrack 的子类 AVMutableCompositionTrack
  • AVMutableComposition 中提供了两个类方法用来获取一个空的 AVMutableComposition 实例对象。
+ (instancetype)composition;
+ (instancetype)compositionWithURLAssetInitializationOptions:(nullable NSDictionary<NSString *, id> *)URLAssetInitializationOptions NS_AVAILABLE(10_11, 9_0);
复制代码
  • 对整个 composition 中的 tracks 的修改方法以下:
//将指定时间段的 asset 中的全部的 tracks 添加到 composition 中 startTime 处
//该方法可能会在 composition 中添加新的 track 以便 asset 中 timeRange 范围中的全部 tracks 都添加到 composition 中
- (BOOL)insertTimeRange:(CMTimeRange)timeRange ofAsset:(AVAsset *)asset atTime:(CMTime)startTime error:(NSError * _Nullable * _Nullable)outError;

//向 composition 中的全部 tracks 添加空的时间范围
- (void)insertEmptyTimeRange:(CMTimeRange)timeRange;

//从 composition 的全部 tracks 中删除一段时间,该操做不会删除 track ,而是会删除与该时间段相交的 track segment
- (void)removeTimeRange:(CMTimeRange)timeRange;

//改变 composition 中的全部的 tracks 的指定时间范围的时长,该操做会改变 asset 的播放速度
- (void)scaleTimeRange:(CMTimeRange)timeRange toDuration:(CMTime)duration;
复制代码
  • 从 composition 中获取 track 或向其中添加/移除 track 方法以下:
//向 composition 中添加一个空的 track ,而且指定媒体资源类型及 trackID 属性值
//若提供的参数 preferredTrackID 无效或为 kCMPersistentTrackID_Invalid ,那么惟一的 trackID 会自动生成
- (AVMutableCompositionTrack *)addMutableTrackWithMediaType:(NSString *)mediaType preferredTrackID:(CMPersistentTrackID)preferredTrackID;

//从 composition 中删除一个指定的 track
- (void)removeTrack:(AVCompositionTrack *)track;

//获取一个与 asset track 相兼容的 composition track 
//为了更好的性能,composition track 的数量应保持最小,这个数量与必需并行播放的媒体数据段数量以及媒体数据的类型相关
//对于可以线性执行且类型相同的媒体数据应使用同一个 composition track ,即便这些数据来自不一样的 asset
- (nullable AVMutableCompositionTrack *)mutableTrackCompatibleWithTrack:(AVAssetTrack *)track;
复制代码
  • AVMutableComposition 中也提供了过滤AVMutableCompositionTrack 的接口:
- (nullable AVMutableCompositionTrack *)trackWithTrackID:(CMPersistentTrackID)trackID;
- (NSArray<AVMutableCompositionTrack *> *)tracksWithMediaType:(NSString *)mediaType;
- (NSArray<AVMutableCompositionTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic;
复制代码

1.4.4 AVCompositionTrack

  • AVCompositionTrack 类同其父类 AVAssetTrack 同样是媒体资源的管理者,它实际是媒体资源数据的集合,它的属性 segments 是 AVCompositionTrackSegment 类的实例对象集合,每一个对象描述一个媒体数据片断。类 AVCompositionTrack 并不经常使用,一般使用的是它的子类 AVMutableCompositionTrack

1.4.5 AVMutableCompositionTrack

  • AVMutableCompositionTrack 中提供的属性以下:
//没有外部数值指定时,媒体1秒钟时间的粒度
@property (nonatomic) CMTimeScale naturalTimeScale;

//当前 track 相关联的语言编码
@property (nonatomic, copy, nullable) NSString *languageCode;

//当前 track 相关联的额外语言编码
@property (nonatomic, copy, nullable) NSString *extendedLanguageTag;

//对于可显示的媒体数据应优先选择的仿射变换设置,默认值为 CGAffineTransformIdentity
@property (nonatomic) CGAffineTransform preferredTransform;

//应优先选择的音量,默认值为 1
@property (nonatomic) float preferredVolume;

//当前track 所包含的全部的媒体数据片断,对于这些片断,它们构成了 track 的完整时间线,
//因此他们的时间线不能够重叠,而且第一个数据片断的时间从 kCMTimeZero 开始,依次日后的时间必须连续不间断、不重叠
@property (nonatomic, copy, null_resettable) NSArray<AVCompositionTrackSegment *> *segments;
复制代码
  • 当咱们获取了一个 AVMutableCompositionTrack 实例对象后,即可以经过如下方法对其进行添加或移除数据片断:
//将已存在的资源文件指定时间范围的媒体数据插入到当前 composition 的指定时间处
//若是 startTime 为 kCMTimeInvalid 值,那么数据被添加到 composition 的最后
- (BOOL)insertTimeRange:(CMTimeRange)timeRange ofTrack:(AVAssetTrack *)track atTime:(CMTime)startTime error:(NSError * _Nullable * _Nullable)outError;

//这个方法与上述方法相似,只是能够批量操做,可是注意提供的时间范围不能重叠
- (BOOL)insertTimeRanges:(NSArray<NSValue *> *)timeRanges ofTracks:(NSArray<AVAssetTrack *> *)tracks atTime:(CMTime)startTime error:(NSError * _Nullable * _Nullable)outError NS_AVAILABLE(10_8, 5_0);

//插入一个没有媒体数据的时间段,当这个范围以前的媒体资源播放结束后,不会马上播放以后的媒体数据,而是会静默一段时间
- (void)insertEmptyTimeRange:(CMTimeRange)timeRange;

//移除一段时间范围的媒体数据,该方法不会致使该 track 从 composition 中移除,只是移除与时间范围相交的数据片断
- (void)removeTimeRange:(CMTimeRange)timeRange;

//改变某个时间范围内的时间的时长,实质是改变了媒体数据的播放速率
//其速率是原时长与现时长的比值,总之,媒体数据是要按时长播放的
- (void)scaleTimeRange:(CMTimeRange)timeRange toDuration:(CMTime)duration;

//判断数据片断的时间线是否重叠
- (BOOL)validateTrackSegments:(NSArray<AVCompositionTrackSegment *> *)trackSegments error:(NSError * _Nullable * _Nullable)outError;
复制代码

1.4.6 AVAssetTrackSegment

  • 媒体资源 AVAsset 中的集合 AVAssetTrack 管理着单条时间线上的媒体数据片断,而每一个数据片断则由 AVAssetTrackSegment 类进行描述。
  • AVAssetTrackSegment 有两个属性:
  1. timeMapping 描述的是数据片断在整个媒体文件中所处的时间范围.timeMapping 是一个结构体,拥有两个成员,对于编辑中的媒体数据片断,它们分别表示数据在源文件中的位置和目标文件中的位置.
  2. empty 描述该数据片断是否为空,若是为空,其 timeMapping.source.start 为 kCMTimeInvalid

1.4.7 AVCompositionTrackSegment

  • 在编辑媒体文件时,在描述数据时,使用的是 AVAssetTrackSegment 的子类 AVCompositionTrackSegment ,它的主要属性和方法以下:
//判断数据片断是否为空,若为空 timeMapping.target 可为有效值,其余为未定义值
@property (nonatomic, readonly, getter=isEmpty) BOOL empty;

//片断数据所处的文件的地址
@property (nonatomic, readonly, nullable) NSURL *sourceURL;

//片断数据所处文件的描述 asset track 的 ID
@property (nonatomic, readonly) CMPersistentTrackID sourceTrackID;

//建立对象,提供了数据片断所在的文件、文件的描述 asset track 的 ID 、源文件中的数据时间范围、目标文件中所处的时间范围
//sourceTimeRange 与 targetTimeRange 的时间长度若是不一致,那么播放的速率会改变
+ (instancetype)compositionTrackSegmentWithURL:(NSURL *)URL trackID:(CMPersistentTrackID)trackID sourceTimeRange:(CMTimeRange)sourceTimeRange targetTimeRange:(CMTimeRange)targetTimeRange;
- (instancetype)initWithURL:(NSURL *)URL trackID:(CMPersistentTrackID)trackID sourceTimeRange:(CMTimeRange)sourceTimeRange targetTimeRange:(CMTimeRange)targetTimeRange NS_DESIGNATED_INITIALIZER;

//建立仅有时间范围而无实际媒体数据的实例
+ (instancetype)compositionTrackSegmentWithTimeRange:(CMTimeRange)timeRange;
- (instancetype)initWithTimeRange:(CMTimeRange)timeRange NS_DESIGNATED_INITIALIZER; 
复制代码

1.5 AVFoundation 之 视音频媒体捕获

AVFoundation 媒体捕捉1

AVFoundation 媒体捕捉2

AVFoundation 媒体捕捉3

  • 摄像机和麦克风的记录输入由捕获会话管理。捕获会话协调从输入设备到输出(如电影文件)的数据流。您能够为单个会话配置多个输入和输出,甚至在会话运行时也是如此。向会话发送消息以启动和中止数据流。此外,您可使用预览层的实例向用户显示摄像机正在录制的内容。

  • 更多关于视频捕获的详情能够参考苹果官方文档:Still and Video Media Capture章节

  • 要管理来自摄像机或麦克风等设备的捕获,您须要组装对象来表示输入和输出,并使用AVCaptureSession实例来协调它们之间的数据流。你须要最低限度:

  1. 表示输入设备的AVCaptureDevice实例,如摄像机或麦克风
  2. AVCaptureInput的一个具体子类的实例,用于配置来自输入设备的端口
  3. AVCaptureOutput的一个具体子类的实例,用于管理电影文件或静态图像的输出
  4. AVCaptureSession的一个实例,用于协调从输入到输出的数据流
  • 要向用户显示摄像机记录内容的预览,可使用AVCaptureVideoPreviewLayer的一个实例(CALayer的一个子类)。

  • 您能够配置多个输入和输出,由单个会话进行协调,如图4-1所示:

    配置多个输入和输出,由单个会话进行协调

  • 对于许多应用程序,这是您须要的尽量多的细节。可是,对于某些操做(例如,若是但愿监视音频通道中的功率级别),须要考虑如何表示输入设备的各个端口,以及如何将这些端口链接到输出。

  • 捕获会话中的捕获输入和捕获输出之间的链接由AVCaptureConnection对象表示。捕获输入(AVCaptureInput的实例)有一个或多个输入端口(AVCaptureInputPort的实例)。捕获输出(AVCaptureOutput的实例)能够接受来自一个或多个源的数据(例如,AVCaptureMovieFileOutput对象同时接受视频和音频数据)。

  • 当您将输入或输出添加到会话时,会话将在全部兼容的捕获输入端口和捕获输出之间造成链接,如图4-2所示。捕获输入和捕获输出之间的链接由AVCaptureConnection对象表示。

AVCaptureConnection表示输入和输出之间的链接

  • 您可使用捕获链接来启用或禁用来自给定输入或到给定输出的数据流。您还可使用链接来监视音频通道中的平均和峰值功率级别。

  • 经过麦克风、摄像机等设备,能够捕获外界的声音和影像。要处理设备捕获的数据,须要使用 AVCaptureDevice 类描述设备,使用 AVCaptureInput 配置数据从设备的输入,使用 AVCaptureOutput 类管理数据到文件的写入,而数据的输入到写出,须要使用 AVCaptureSession 类进行协调。此外,可使用 AVCaptureVideoPreviewLayer 类显示相机正在拍摄的画面。

  • 一个设备能够有多个输入,使用 AVCaptureInputPort 类描述这些输入,用 AVCaptureConnection 类描述具体类型的输入与输出的关系,能够实现更精细的数据处理。

1.5.1 AVCaptureSession

  • `AVCaptureSession 是捕获视听数据的核心类,它协调数据的输入和输出。建立一个 AVCaptureSession 类的对象时,能够指定最终获得的视听数据的质量,固然这个质量与设备也有关系,一般在设置以前,能够调用方法判断 session 是否支持要设置的质量。

  • AVCaptureSession 类实例可设置的数据质量有 AVCaptureSessionPresetHighAVCaptureSessionPresetMediumAVCaptureSessionPresetLowAVCaptureSessionPreset320x240 等。在进行设置以前,能够调用 AVCaptureSession 中的方法进行校验。

- (BOOL)canSetSessionPreset:(NSString*)preset;
复制代码
  • 设置好对象后,可调用下面的方法,添加、移除输入、输出。
- (BOOL)canAddInput:(AVCaptureInput *)input;
- (void)addInput:(AVCaptureInput *)input;
- (void)removeInput:(AVCaptureInput *)input;

- (BOOL)canAddOutput:(AVCaptureOutput *)output;
- (void)addOutput:(AVCaptureOutput *)output;
- (void)removeOutput:(AVCaptureOutput *)output;

- (void)addInputWithNoConnections:(AVCaptureInput *)input NS_AVAILABLE(10_7, 8_0);
- (void)addOutputWithNoConnections:(AVCaptureOutput *)output NS_AVAILABLE(10_7, 8_0);

- (BOOL)canAddConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 8_0);
- (void)addConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 8_0);
- (void)removeConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 8_0);
复制代码
  • 开始执行 session 或者结束执行,调用下面的方法:
- (void)startRunning;
- (void)stopRunning;
复制代码
  • 对于正在执行中的 session ,要对其进行改变,所做出的改变,应放在下面两个方法之间。
- (void)beginConfiguration;
- (void)commitConfiguration;
复制代码
  • AVCaptureSession 开始执行、结束执行、执行过程当中出错或被打断时,都会发出通知,经过注册下面的通知,能够获取咱们感兴趣的信息。
  1. VCaptureSessionRuntimeErrorNotification 经过 AVCaptureSessionErrorKey 能够获取出错的缘由
  2. AVCaptureSessionDidStartRunningNotification 开始 session
  3. AVCaptureSessionDidStopRunningNotification 结束 session
  4. AVCaptureSessionWasInterruptedNotification 经过 AVCaptureSessionInterruptionReasonKey 能够获取被打断的缘由
  5. AVCaptureSessionInterruptionEndedNotification 打断结束,session 从新开始

1.5.2 AVCaptureDevice

  • AVCaptureDevice是用来描述设备属性的类,要捕获视听数据,须要获取相应的设备,使用该类获取有效的设备资源。这个设备资源列表是随时变更的,其在变更时,会发送AVCaptureDeviceWasConnectedNotificationAVCaptureDeviceWasDisconnectedNotification 通知,以告知有设备链接或断开。

  • 在获取设备以前,要先肯定要获取的设备的类型 AVCaptureDeviceType ,设备的位置 AVCaptureDevicePosition ,也能够经过要获取的媒体数据类型进行设备的选择。

  • 获取设备后,能够保存它的惟一标识、模型标识、名称等信息,以待下次用来获取设备。

+ (NSArray *)devices;
+ (NSArray *)devicesWithMediaType:(NSString *)mediaType;
+ (AVCaptureDevice *)defaultDeviceWithMediaType:(NSString *)mediaType;
+ (AVCaptureDevice *)deviceWithUniqueID:(NSString *)deviceUniqueID;

@property(nonatomic, readonly) NSString *uniqueID;
@property(nonatomic, readonly) NSString *modelID;
@property(nonatomic, readonly) NSString *localizedName;

//校验得到的设备可否提供相应的媒体数据类型
- (BOOL)hasMediaType:(NSString *)mediaType;

//校验得到的设备可否支持相应的配置
- (BOOL)supportsAVCaptureSessionPreset:(NSString *)preset;

复制代码
  • 获取一个设备后,能够经过修改它的属性来知足本身的须要。
  1. flashMode 闪光灯的模式(AVCaptureFlashModeOff 、AVCaptureFlashModeOn 、AVCaptureFlashModeAuto)
  2. torchMode 手电筒的模式(AVCaptureTorchModeOff 、AVCaptureTorchModeOn 、AVCaptureTorchModeAuto) torchLevel 手电筒的亮度(0~1)
  3. focusMode 聚焦模式(AVCaptureFocusModeLocked 、AVCaptureFocusModeAutoFocus 、AVCaptureFocusModeContinuousAutoFocus)
  4. exposureMode 曝光模式(AVCaptureExposureModeLocked 、AVCaptureExposureModeAutoExpose 、AVCaptureExposureModeContinuousAutoExposure 、AVCaptureExposureModeCustom)
  5. whiteBalanceMode 白平衡模式(AVCaptureWhiteBalanceModeLocked 、AVCaptureWhiteBalanceModeAutoWhiteBalance 、AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance)
  • 在修改这些属性时,应先判断当前设备是否支持要设置的属性值,而且全部的属性修改都要放在下面两个方法之间,以保证属性可以被正确设置。
- (BOOL)lockForConfiguration:(NSError **)outError;
- (void)unlockForConfiguration;
复制代码
  • 在调用硬件设备以前,应先判断应用是否拥有相应的权限,其权限分为如下几种:
  1. AVAuthorizationStatusNotDetermined 未定义
  2. AVAuthorizationStatusRestricted 无权限(因某些缘由,系统拒绝权限)
  3. AVAuthorizationStatusDenied 无权限(用户拒绝)
  4. AVAuthorizationStatusAuthorized 有权限
//校验权限
+ (AVAuthorizationStatus)authorizationStatusForMediaType:(NSString *)mediaType NS_AVAILABLE_IOS(7_0);

//请求权限,handler 处理会在任意线程中执行,因此须要在主线程中执行的处理由用户负责指定
+ (void)requestAccessForMediaType:(NSString *)mediaType completionHandler:(void (^)(BOOL granted))handler NS_AVAILABLE_IOS(7_0);
复制代码

1.5.3 AVCaptureDeviceInput

  • AVCaptureDeviceInputAVCaptureInput 的子类,使用一个 AVCaptureDevice 类实例建立该类的实例,其管理设备的输入。
  • 在建立了实例对象后,将其添加到 session 中。
NSError *error;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (input && [session canAddInput:input]) {
    [captureSession addInput:captureDeviceInput];
}
复制代码

1.5.4 AVCaptureOutput

  • AVCaptureOutput 是一个抽象类,一般使用的是它的子类
  1. AVCaptureMovieFileOutput 用来生成一个影视文件
  2. AVCaptureVideoDataOutput 用来处理输入的视频的帧
  3. AVCaptureAudioDataOutput 用来处理音频数据
  4. AVCaptureStillImageOutput 用来获取图片
  • 在建立了具体的子类后,将它添加到 session 中
AVCaptureMovieFileOutput *movieOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([session canAddOutput:movieOutput]) {
    [session addOutput:movieOutput];
}
复制代码

1.5.5 AVCaptureFileOutput

  • AVCaptureFileOutputAVCaptureOutput 的子类,是 AVCaptureMovieFileOutputAVCaptureAudioFileOutput 的父类。这个类中定义了文件输出时的地址、时长、容量等属性。
//当前记录的数据的文件的地址
@property(nonatomic, readonly) NSURL *outputFileURL;

//开始文件的记录,指定文件的地址,以及记录过程当中或结束时要通知的代理对象
//指定的 outputFileURL 必需是有效的且没有文件占用
- (void)startRecordingToOutputFileURL:(NSURL*)outputFileURL recordingDelegate:(id<AVCaptureFileOutputRecordingDelegate>)delegate;

//该方法能够中止数据向文件中写入
//若是要中止一个文件的写入转而指定另外一个文件的写入,不该调用该方法,只需直接调用上面的方法
//当因该方法的调用、出错、或写入文件的变动致使当前文件开始中止写入时,最后传入的缓存数据仍会在后台被写入
//不管什么时候,要使用文件,都须要等指定的代理对象被告知文件的写入已经结束以后进行
- (void)stopRecording;

//判断当前是否有数据被写入文件
@property(nonatomic, readonly, getter=isRecording) BOOL recording;

//表示到目前为止,当前文件已经记录了多长时间
@property(nonatomic, readonly) CMTime recordedDuration;

//表示到目前为止,当前文件已经记录了多少个字节
@property(nonatomic, readonly) int64_t recordedFileSize;    
/** 下面三个值对文件的记录进行了限制,若果达到限制,则会在回调方法 captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: 中传递相应的错误 */
//表示当前文件可以记录的最长时间,kCMTimeInvalid 表示无时间限制
@property(nonatomic) CMTime maxRecordedDuration;

//表示当前文件可以记录的最大字节数,0 表示无大小限制
@property(nonatomic) int64_t maxRecordedFileSize;

//表示记录当前文件时须要保留的最小字节数
@property(nonatomic) int64_t minFreeDiskSpaceLimit;

//在 Mac OS X 系统下,经过指定遵循 AVCaptureFileOutputDelegate 协议的代理对象,来实现缓存数据的精确记录
@property(nonatomic, assign) id<AVCaptureFileOutputDelegate> delegate NS_AVAILABLE(10_7, NA);

/** 在 Mac OS X 系统下,这个属性和方法能够判断记录是否中止,以及控制数据向文件中的中止写入和从新开始写入 */
@property(nonatomic, readonly, getter=isRecordingPaused) BOOL recordingPaused NS_AVAILABLE(10_7, NA);
- (void)pauseRecording NS_AVAILABLE(10_7, NA);
- (void)resumeRecording NS_AVAILABLE(10_7, NA);
复制代码

1.5.6 AVCaptureFileOutputRecordingDelegate

  • AVCaptureFileOutputRecordingDelegate 是文件记录过程当中须要用到的协议,它一般的做用是告知代理对象文件记录结束了。
//这个代理方法是遵循该协议的代理对象必需要实现的方法
//每个文件记录请求,最终都会调用这个方法,即便没有数据成功写入文件
//当 error 返回时,文件也可能成功保存了,应检查 error 中的 AVErrorRecordingSuccessfullyFinishedKey 信息,查看具体错误
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error;

//当数据写入文件后调用,若是数据写入失败,该方法可能不会被调用
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections;

/** 在 Mac OS X 系统下,当文件的记录被暂停或从新开始,会调用下面的方法,若是记录被终止,不会调用下面的方法 */
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didPauseRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections NS_AVAILABLE(10_7, NA);
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didResumeRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections NS_AVAILABLE(10_7, NA);

//在 Mac OS X 系统下,当记录将被中止,不管是主动的仍是被动的,都会调用下面的方法
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput willFinishRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections error:(NSError *)error NS_AVAILABLE(10_7, NA);
复制代码

1.5.7 AVCaptureFileOutputDelegate

  • AVCaptureFileOutputDelegate 这个协议只用于 Mac OS X 系统下,它给了客户端精准操控数据的机会。
/** 在 Mac OS X 10.8 系统以前,实现代理方法 captureOutput:didOutputSampleBuffer:fromConnection: 后即可以在该方法中实现数据记录的准确开始或结束,而要实如今任一一个画面帧处开始或中止数据的记录,要对每收到的 帧数据进行预先处理,这个过程消耗电能、产生热量、占用 CPU 资源,因此在 Mac OS X 10.8 及其以后的系统,提供了 下面的代理方法,来肯定客户端需不须要随时进行记录的开始或中止。 若是这个方法返回 NO ,对数据记录的设置将在开启记录以后进行。 */
- (BOOL)captureOutputShouldProvideSampleAccurateRecordingStart:(AVCaptureOutput *)captureOutput NS_AVAILABLE(10_8, NA);

/** 若是上面的方法返回了 YES ,那么客户端即可以使用下面的方法对每个视频帧数据或音频数据进行操做 为了提升性能,缓存池中的缓存变量的内存一般会被复用,若是长时间使用缓存变量,那么新的缓存数据没法复制到 相应的内存中便会被废弃,因此若须要长时间使用缓存数据 sampleBuffer ,应复制一份,使其自己可以被系统复用 */
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, NA);
复制代码

1.5.8 AVCaptureAudioFileOutput

  • AVCaptureAudioFileOutput 是 AVCaptureFileOutput 的子类,该类用于将媒体数据记录为一个音频文件。
//返回该类支持的音频文件类型
+ (NSArray *)availableOutputFileTypes;

//开始记录音频文件
- (void)startRecordingToOutputFileURL:(NSURL*)outputFileURL outputFileType:(NSString *)fileType recordingDelegate:(id<AVCaptureFileOutputRecordingDelegate>)delegate;

//要写入音频文件中的元数据 AVMetadataItem 集合
@property(nonatomic, copy) NSArray *metadata; 

//写入的音频文件的设置 AVAudioSettings.h
@property(nonatomic, copy) NSDictionary *audioSettings;
复制代码

1.5.9 AVCaptureVideoDataOutput

  • AVCaptureVideoDataOutputAVCaptureOutput 的子类,该类能够用来处理捕获的每个视频帧数据。建立一个该类的实例对象后,要调用下面的方法设置一个代理对象,及调用代理对象所实现的协议方法的队列。
- (void)setSampleBufferDelegate:(id<AVCaptureVideoDataOutputSampleBufferDelegate>)sampleBufferDelegate queue:(dispatch_queue_t)sampleBufferCallbackQueue;
复制代码
  • 指定的队列 sampleBufferCallbackQueue 必需是串行队列以保证传递的帧是按记录时间前后传递的。
//设置输出的视频要进行怎样的格式处理
//设置为空([NSDictionary dictionary])表示不改变输入时的视频格式
//设置为 nil 表示未压缩格式
@property(nonatomic, copy) NSDictionary *videoSettings;

//获取 kCVPixelBufferPixelFormatTypeKey 的有效值
@property(nonatomic, readonly) NSArray *availableVideoCVPixelFormatTypes NS_AVAILABLE(10_7, 5_0);

//获取 AVVideoCodecKey 的有效值
@property(nonatomic, readonly) NSArray *availableVideoCodecTypes NS_AVAILABLE(10_7, 5_0);

//表示当回调队列阻塞时,是否马上丢弃新接收的帧数据
@property(nonatomic) BOOL alwaysDiscardsLateVideoFrames;
复制代码

1.5.10 AVCaptureVideoDataOutputSampleBufferDelegate

  • 该协议用来处理接收的每个帧数据,或者提示客户端有帧数据被丢弃。
//接收到一个帧数据时,在指定的串行队列中调用该方法,携带帧数据并包含有其余帧信息
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;

//丢弃一个帧数据时,在指定的串行队列中调用该方法,sampleBuffer 只携带帧信息,具体帧数据并未携带
- (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 6_0);
复制代码

1.5.11 AVCaptureVideoPreviewLayer

  • AVCaptureVideoPreviewLayerCALayer 的子类,使用该类能够实现捕获视频的显示。使用一个 session 建立一个该类对象,然后将该类对象插入到图层树中,从而显示捕获的视频。
//建立方法
+ (instancetype)layerWithSession:(AVCaptureSession *)session;
- (instancetype)initWithSession:(AVCaptureSession *)session;
复制代码
  • 修改 AVCaptureVideoPreviewLayer 的属性 videoGravity 值,能够选择显示捕获视频时的界面大小变化方式,它有如下可选值:
  1. AVLayerVideoGravityResize 默认值,直接铺满屏幕,及时画面变形
  2. AVLayerVideoGravityResizeAspect 保持画面的横纵比,不铺满屏幕,多余的空间显示黑色
  3. AVLayerVideoGravityResizeAspectFill 保持画面的横纵比,铺满屏幕,多余的画面进行裁剪

1.5.12 AVCaptureAudioDataOutput

  • AVCaptureAudioDataOutputAVCaptureOutput 的子类,该类能够处理接收到的音频数据。同 AVCaptureVideoDataOutput 相似,该类也提供了一个方法,用于设置代理对象,以及调用代理对象实现的协议方法时的队列。
- (void)setSampleBufferDelegate:(id<AVCaptureAudioDataOutputSampleBufferDelegate>)sampleBufferDelegate queue:(dispatch_queue_t)sampleBufferCallbackQueue;
复制代码

1.5.13 AVCaptureAudioDataOutputSampleBufferDelegate

  • 该协议提供了一个方法,用来实现对音频数据的接收处理。
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;

复制代码

1.5.14 AVAssetImageGenerator

  • 使用 AVAssetImageGenerator 生成视频资源的缩略图,使用 AVAsset 对象建立 AVAssetImageGenerator 对象,可使用类方法或实例方法,以下:
+ (instancetype)assetImageGeneratorWithAsset:(AVAsset *)asset;
- (instancetype)initWithAsset:(AVAsset *)asset NS_DESIGNATED_INITIALIZER;

复制代码
  • 固然,在此以前,最好调用 AVAsset 中的方法 - (NSArray<AVAssetTrack *> *)tracksWithMediaCharacteristic:(NSString *)mediaCharacteristic; 来判断 asset 中是否有可视媒体数据。若是有,那么再建立 AVAssetImageGenerator 对象,然后再调用下面的方法,来获取一张或多张图片。
//获取一张图片,requestedTime 指定要获取视频中哪一个时刻的图片,actualTime 返回图片实际是视频的哪一个时刻,outError 返回错误信息
- (nullable CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(nullable CMTime *)actualTime error:(NSError * _Nullable * _Nullable)outError CF_RETURNS_RETAINED;

//获取多张图片,每一次图片生成后,都会调用一次 handler
- (void)generateCGImagesAsynchronouslyForTimes:(NSArray<NSValue *> *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler;

//上述 handler 的类型以下,回调中的参数有图片的请求时刻和实际时刻,图片,状态(成功、失败、取消),错误信息
typedef void (^AVAssetImageGeneratorCompletionHandler)(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error);
复制代码

1.6 AVFoundation 之媒体流输出

AVFoundation 视频音频输出

  • 对媒体数据资源进行简单的转码或裁剪,使用 AVAssetExportSession 类便足够了,可是更深层次的修改媒体资源,便须要用到 AVAssetReader 类和 AVAssetWriter 类。
  • AVAssetReader 只能与一个资源 asset 相关联,且不能用来读取实时数据,在开始读取数据以前,须要为 reader 添加 AVAssetReaderOutput 的实例对象。这个实例对象描述的是待读取的数据资源来源类型,一般使用 AVAssetReaderAudioMixOutputAVAssetReaderTrackOutputAVAssetReaderVideoCompositionOutput 三种子类。
  • AVAssetWriter 能够未来自多个数据源的数据以指定的格式写入到一个指定的文件中,且其只能对应一个文件。在写文件以前,须要用每个 AVAssetWriterInput 类实例对象来描述相应的数据源。每个 AVAssetWriterInput 实例对象接收的数据都应是 CMSampleBufferRef 类型的变量。若是使用 AVAssetWriterInputPixelBufferAdaptor 类也能够直接将 CVPixelBufferRef 类型的变量数据添加到 writer input 中。
  • AVAssetReaderAVAssetWriter 结合起来使用,即可以对读取的数据进行相应的编辑修改,然后写入到一个文件中并保存。

1.6.1 AVAssetReader

  • 使用该类读取媒体资源,其提供的初始化方法与一个 asset 相关联。
//对于提供的参数 asset ,若是是可被修改的,那么在开始读取操做后,对其进行了修改,以后的读取操做都是无效的
+ (nullable instancetype)assetReaderWithAsset:(AVAsset *)asset error:(NSError * _Nullable * _Nullable)outError;
- (nullable instancetype)initWithAsset:(AVAsset *)asset error:(NSError * _Nullable * _Nullable)outError NS_DESIGNATED_INITIALIZER;

//当前读取操做的状态,可取值有 AVAssetReaderStatusUnknown 、AVAssetReaderStatusReading 、
AVAssetReaderStatusCompletedAVAssetReaderStatusFailedAVAssetReaderStatusCancelled
@property (readonly) AVAssetReaderStatus status;
//当 status 的值为 AVAssetReaderStatusFailed 时,描述错误信息
@property (readonly, nullable) NSError *error;


//限制可读取的资源的时间范围
@property (nonatomic) CMTimeRange timeRange;

//判断可否添加该数据源
- (BOOL)canAddOutput:(AVAssetReaderOutput *)output;
//添加数据源
- (void)addOutput:(AVAssetReaderOutput *)output;

//开始读取
- (BOOL)startReading;
//结束读取
- (void)cancelReading;
复制代码

1.6.2 AVAssetReaderOutput

  • AVAssetReaderOutput 是用来描述待读取的数据的抽象类,读取资源时,应建立该类的对象,并添加到相应的 AVAssetReader 实例对象中去。
//获取的媒体数据的类型
@property (nonatomic, readonly) NSString *mediaType;

//是否拷贝缓存中的数据到客户端,默认 YES ,客户端能够随意修改数据,可是为优化性能,一般设为 NO
@property (nonatomic) BOOL alwaysCopiesSampleData NS_AVAILABLE(10_8, 5_0);

//同步获取下一个缓存数据,使用返回的数据结束后,应使用 CFRelease 函数将其释放
//当错误或没有数据可读取时,返回 NULL ,返回空后,应检查相关联的 reader 的状态
- (nullable CMSampleBufferRef)copyNextSampleBuffer CF_RETURNS_RETAINED;

//是否支持从新设置数据的读取时间范围,即可否修改 reader 的 timeRange 属性
@property (nonatomic) BOOL supportsRandomAccess NS_AVAILABLE(10_10, 8_0);
//设置从新读取的时间范围,这个时间范围集合中的每个时间范围的开始时间必需是增加的且各个时间范围不能重叠
//应在 reader 调用 copyNextSampleBuffer 方法返回 NULL 以后才可调用
- (void)resetForReadingTimeRanges:(NSArray<NSValue *> *)timeRanges NS_AVAILABLE(10_10, 8_0);
//该方法调用后,上面的方法即不可再调用,同时 reader 的状态也不会被阻止变为 AVAssetReaderStatusCompleted 了
- (void)markConfigurationAsFinal NS_AVAILABLE(10_10, 8_0);
复制代码

1.6.3 AVAssetReaderTrackOutput

  • AVAssetReaderTrackOutputAVAssetReaderOutput 的子类,它用来描述待读取的数据来自 asset track ,在读取前,还能够对数据的格式进行修改。
//初始化方法,参数中指定了 track 和 媒体的格式
//指定的 track 应在 reader 的 asset 中
+ (instancetype)assetReaderTrackOutputWithTrack:(AVAssetTrack *)track outputSettings:(nullable NSDictionary<NSString *, id> *)outputSettings;
- (instancetype)initWithTrack:(AVAssetTrack *)track outputSettings:(nullable NSDictionary<NSString *, id> *)outputSettings NS_DESIGNATED_INITIALIZER;

//指定音频处理时的算法
@property (nonatomic, copy) NSString *audioTimePitchAlgorithm NS_AVAILABLE(10_9, 7_0);
复制代码

1.6.4 AVAssetReaderAudioMixOutput

  • AVAssetReaderAudioMixOutputAVAssetReaderOutput 的子类,它用来描述待读取的数据来自音频组合数据。建立该类实例对象提供的参数 audioTracks 集合中的每个 asset track 都属于相应的 reader 中的 asset 实例对象,且类型为 AVMediaTypeAudio 。
  • 参数 audioSettings 给出了音频数据的格式设置。
+ (instancetype)assetReaderAudioMixOutputWithAudioTracks:(NSArray<AVAssetTrack *> *)audioTracks audioSettings:(nullable NSDictionary<NSString *, id> *)audioSettings;
- (instancetype)initWithAudioTracks:(NSArray<AVAssetTrack *> *)audioTracks audioSettings:(nullable NSDictionary<NSString *, id> *)audioSettings NS_DESIGNATED_INITIALIZER
复制代码
  • 此外,该类的 audioMix 属性,描述了从多个 track 中读取的音频的音量变化状况:@property (nonatomic, copy, nullable) AVAudioMix *audioMix;

1.6.5 AVAssetReaderVideoCompositionOutput

  • AVAssetReaderVideoCompositionOutputAVAssetReaderOutput 的子类,该类用来表示要读取的类是组合的视频数据。 同 AVAssetReaderAudioMixOutput 相似,该类也提供了两个建立实例的方法,须要提供的参数的 videoTracks 集合中每个 track 都是 与 reader 相关联的 asset 中的 track 。
+ (instancetype)assetReaderVideoCompositionOutputWithVideoTracks:(NSArray<AVAssetTrack *> *)videoTracks videoSettings:(nullable NSDictionary<NSString *, id> *)videoSettings;
- (instancetype)initWithVideoTracks:(NSArray<AVAssetTrack *> *)videoTracks videoSettings:(nullable NSDictionary<NSString *, id> *)videoSettings NS_DESIGNATED_INITIALIZER;
复制代码
  • 该类的属性 videoComposition 一样描述了每一个 track 的帧的显示方式。
@property (nonatomic, copy, nullable) AVVideoComposition *videoComposition;
复制代码

1.7 AVFoundation 之媒体的时间和数据

AVFoundation 媒体的时间与数据

相关文章
相关标签/搜索