IOS音视频(一)AVFoundation核心类 1. AVFoundation框架架构简介 1.1 AVFoundation框架 1.2 AVFoundation 之 Assets 1.3 AVFoundation 之 视频播放 1.3.1 AVPlayer 1.3.2 播放不同类型的资源 1.3.3 播放控制 1.3.4 自定义播放--音频 1.3.4.1 AVAudioMix 1.3.4.2 AVMutableAudioMix 1.3.4.3 AVAudioMixInputParameters 1.3.4.4 AVMutableAudioMixInputParameters 1.3.5 自定义播放--视频 1.3.5.1 AVVideoComposition 1.3.5.2 AVMutableVideoComposition 1.3.5.3 AVVideoCompositionInstruction 1.3.5.4 AVMutableVideoCompositionInstruction 1.3.5.5 AVVideoCompositionLayerInstruction 1.3.5.6 AVMutableVideoCompositionLayerInstruction 1.3.5.7 AVVideoCompositionCoreAnimationTool 1.3.5.8 AVVideoCompositionValidationHandling 1.3.5.9 AVVideoCompositionValidationHandling 1.4 AVFoundation 之 视音频编辑 1.4.1 AVAssetExportSession 1.4.2 AVComposition 1.4.3 AVMutableComposition 1.4.4 AVCompositionTrack 1.4.5 AVMutableCompositionTrack 1.4.6 AVAssetTrackSegment 1.4.7 AVCompositionTrackSegment 1.5 AVFoundation 之 视音频媒体捕获 1.5.1 AVCaptureSession 1.5.2 AVCaptureDevice 1.5.3 AVCaptureDeviceInput 1.5.4 AVCaptureOutput 1.5.5 AVCaptureFileOutput 1.5.6 AVCaptureFileOutputRecordingDelegate 1.5.7 AVCaptureFileOutputDelegate 1.5.8 AVCaptureAudioFileOutput 1.5.9 AVCaptureVideoDataOutput 1.5.10 AVCaptureVideoDataOutputSampleBufferDelegate 1.5.11 AVCaptureVideoPreviewLayer 1.5.12 AVCaptureAudioDataOutput 1.5.13 AVCaptureAudioDataOutputSampleBufferDelegate 1.5.14 AVAssetImageGenerator 1.6 AVFoundation 之媒体流输出 1.6.1 AVAssetReader 1.6.2 AVAssetReaderOutput 1.6.3 AVAssetReaderTrackOutput 1.6.4 AVAssetReaderAudioMixOutput 1.6.5 AVAssetReaderVideoCompositionOutput 1.7 AVFoundation 之媒体的时间和数据
做音视频开发是个很复杂的工作,需要我们理解很多有关素材的知识:声学和视觉相关的科学理论,数的程序开发技术和有AVFoundation框架而引出的其他框架的知识,比如:Core Media, Core Video, Core Image, Core Audio, Media Player 和 VideoToolbox 等等。
要做IOS音视频相关的开发,肯定要熟悉AVFoundation框架。学习框架最好的方式就是研究苹果官方文档:苹果官方AVFoundation框架介绍
AVFoundation 是 Objective-C 中创建及编辑视听媒体文件的几个框架之一,其提供了检查、创建、编辑或重新编码媒体文件的接口,也使得从设备获取的视频实时数据可操纵。但是,通常情况,简单的播放或者录像,直接使用 AVKit 框架或者 UIImagePickerController 类即可。另外,值得注意的是,在 AVFoundation 框架中使用的基本数据结构,如时间相关的或描述媒体数据的数据结构都声明在 CoreMedia 框架中。
AVFoundation 是 OSX 系统和 iOS 系统中用于处理基于时间的媒体数据的高级 objectivec 框架,其设计过程高度依赖多线程机制,充分利用了多核硬件优势,大量使用 Block 和 GCD 机制。AVFoundation 能与高层级框架无缝衔接,也能提供低层级框架的功能和性能。
1. 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框架,可以用来检测,编辑,创建,重新编码媒体文件,还可以实时获取设备的流媒体数据,实时操作这些被捕获的视频流数据。 苹果推荐我们尽可能的使用高度抽象的接口:
如果只是简单播放视频文件,使用AVKit框架即可。 如果只是想简单录制视频,使用UIKit框架里的UIImagePickerController既可以实现。
AVFoundation 框架包含视频相关的接口以及音频相关的接口,与音频相关的类有 AVAudioPlayer、AVAudioRecorder、AVAudioSession。
1.1 AVFoundation框架
AVFoundation 提供的核心功能如下所示
音频播放和记录——AVAudioPlayer 和 AVAudioRecorder 媒体文件检查——AVMetadataItem 视频播放——AVPlayer 和 AVPlayerItem 媒体捕捉——AVCaptureSession 媒体编辑 媒体处理——AVAssetReader、AVAssetWriter
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
参考苹果官方介绍
AVAsset 是AVFoundation框架里的一个核心类,主要提供了一种形式独立的基于时间的视听数据抽象,例如电影文件或视频流。
AVAsset 包含需要一起呈现或处理的音轨集合,每个音轨都是统一的媒体类型,包括(但不限于)音频、视频、文本、封闭字幕和字幕。asset对象提供关于整个资源的信息,比如它的持续时间或标题,以及表示的提示,比如它的大小。AVAsset 也可以有元数据,由AVMetadataItem的实例表示。
跟踪由AVAssetTrack的实例表示,在一个典型的简单例子中,一个轨道表示音频组件,另一个表示视频组件;在一个复杂的组合中,音频和视频可能会有多个重叠的音轨。
音轨具有许多属性,例如其类型(视频或音频)、可视和/或可听特征(视情况而定)、元数据和时间轴(以其父资产的形式表示)。磁道也有一组格式说明。数组包含CMFormatDescription对象(参见CMFormatDescriptionRef),每个对象描述曲目引用的媒体样本的格式。包含统一媒体的磁道(例如,所有使用相同设置编码的磁道)将提供一个计数为1的数组。
一个磁道本身可以被分割成段,用AVAssetTrackSegment的实例来表示。段是从源到资产跟踪时间线的时间映射。
CMTime是一个C结构,它将时间表示为一个有理数,具有一个分子(int64_t值)和一个分母(int32_t时间刻度)。从概念上讲,时间刻度指定了分子中每个单位所占的秒数。因此,如果时间刻度是4,每个单元代表四分之一秒;如果时间刻度是10,每个单元表示十分之一秒,以此类推。您经常使用600的时间刻度,因为这是几种常用帧率的倍数:24帧用于电影,30帧用于NTSC(用于北美和日本的电视),25帧用于PAL(用于欧洲的电视)。使用600的时间刻度,您可以精确地表示这些系统中的任意数量的帧。除了简单的时间值之外,CMTime结构还可以表示非数值:+∞、-∞和不定。它还可以指示时间是否在某个点被四舍五入,并保持一个历元数。
您可以使用CMTimeMake或CMTimeMakeWithSeconds等相关函数之一创建时间(该函数允许您使用浮点值创建时间并指定首选时间刻度)。有几个函数用于基于时间的算术和比较时间,如下例所示:
CMTime time1
= CMTimeMake ( 200 , 2 ) ;
CMTime time2
= CMTimeMake ( 400 , 4 ) ;
if ( CMTimeCompare ( time1
, time2
) == 0 ) { NSLog ( @
"time1 and time2 are the same" ) ;
} Float64 float64Seconds
= 200.0 / 3 ;
CMTime time3
= CMTimeMakeWithSeconds ( float64Seconds
, 3 ) ;
time3
= CMTimeMultiply ( time3
, 3 ) ;
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 : break ; case AVKeyValueStatusLoading : break ; case AVKeyValueStatusLoaded : break ; case AVKeyValueStatusFailed : break ; case AVKeyValueStatusCancelled : break ; }
} ] ;
1.3 AVFoundation 之 视频播放
1.3.1 AVPlayer
具体可以参考苹果官方介绍的Playback章节
要控制媒体资源的回放,可以使用AVPlayer对象。在回放期间,您可以使用AVPlayerItem实例来管理整个Asset的表示状态,使用AVPlayerItemTrack对象来管理单个曲目的表示状态。要显示视频,可以使用AVPlayerLayer对象。
player是一个控制器对象,您可以使用它来管理资产的回放,例如启动和停止回放,以及查找特定的时间。您使用AVPlayer实例来播放单个资产。您可以使用AVQueuePlayer对象按顺序播放许多项(AVQueuePlayer是AVPlayer的子类)。在OS X上,你可以选择使用AVKit框架的AVPlayerView类来回放视图中的内容。
播放器向您提供有关播放状态的信息,因此,如果需要,您可以将用户界面与播放器的状态同步。通常将播放器的输出定向到特定的核心动画层(AVPlayerLayer或AVSynchronizedLayer的实例)。有关层的更多信息,请参见Core Animation Programming Guide。
尽管最终你想要播放资产,但你不能直接提供资产给AVPlayer 对象。而是提供AVPlayerItem的一个实例。播放器项管理与其相关联的资产的表示状态。播放器项包含播放器项跟踪—avplayeritemtrack的实例—对应于资产中的跟踪。各对象之间的关系如图所示:
这种抽象意味着您可以同时使用不同的玩家来玩给定的资产,但是每个玩家呈现的方式不同。图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 后,AVAsset 和 AVAssetTrack 对象将由 item 对象创建。
1.3.3 播放控制
通过调用 player 的 play 、pause 、setRate: 方法,可以控制 item 的播放,这些方法都会改变 player 的属性 rate 的值,该值为 1 表示 item 按正常速率播放,为 0 表示 item 暂停播放,0~1 表示低速播放,大于 1 表示高速播放,小于 0 表示从后向前播放。 item 的属性 timeControlStatus 的值表示当前 item 的状态,有下面 3 个值:
AVPlayerTimeControlStatusPaused 暂停 AVPlayerTimeControlStatusPlaying 播放 AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate 等待按指定速率播放状态,该状态是当 rate 的值设置为非 0 值时,而 item 因某些原因还无法播放的情况,而无法播放的原因,可依通过 item 的 reasonForWaitingToPlay 属性值查看。
item 的属性 actionAtItemEnd 的值表示当前 item 播放结束后的动作,有下面 3 个值:
AVPlayerActionAtItemEndAdvance 只适用于 AVQueuePlayer 类,表示播放队列中的下一个 item AVPlayerActionAtItemEndPause 表示暂停 AVPlayerActionAtItemEndNone 表示无操作,当前 item 的 currentTime 属性值仍然按 rate 的值改变 item 的 currentTime 属性值表示当前 item 的播放时间,可以调用下面的方法指定 item 从何处进行播放。
- ( void
) seekToDate
: ( NSDate * ) date
;
- ( void
) seekToTime
: ( CMTime ) time toleranceBefore
: ( CMTime ) toleranceBefore toleranceAfter
: ( CMTime ) toleranceAfter
; ( tolerance
: 公差,前后公差
)
- ( 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
AVMutableAudioMix 是 AVAudioMix 的子类,它的方法 audioMix 返回一个 inputParameters 属性为空的实例。
1.3.4.3 AVAudioMixInputParameters
这个类是音量变化的描述类,它同一个音频的 track 相关联,并设置音量随时间变化的算法,其获取音量变化的方法如下:
- ( BOOL ) getVolumeRampForTime
: ( CMTime ) time startVolume
: ( nullable float
* ) startVolume endVolume
: ( nullable float
* ) endVolume timeRange
: ( nullable
CMTimeRange * ) timeRange
;
1.3.4.4 AVMutableAudioMixInputParameters
AVMutableAudioMixInputParameters 是 AVAudioMixInputParameters 的子类,它提供了直接设置某个时刻或时间段的音量的方法。
+ ( instancetype
) audioMixInputParametersWithTrack
: ( nullable
AVAssetTrack * ) track
;
+ ( 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 的父类,它的主要属性和方法如下:
@property
( nonatomic
, readonly
, nullable
) Class < AVVideoCompositing > customVideoCompositorClass
NS_AVAILABLE ( 10_9 , 7_0 ) ;
@property
( nonatomic
, readonly
) CMTime frameDuration
;
@property
( nonatomic
, readonly
) CGSize renderSize
;
@property
( nonatomic
, readonly
) float renderScale
;
@property
( nonatomic
, readonly
, copy
) NSArray < id
< AVVideoCompositionInstruction > > * instructions
;
@property
( nonatomic
, readonly
, retain
, nullable
) AVVideoCompositionCoreAnimationTool * animationTool
;
+ ( AVVideoComposition * ) videoCompositionWithPropertiesOfAsset
: ( AVAsset * ) asset
NS_AVAILABLE ( 10_9 , 6_0 ) ;
@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 ) ;
+ ( AVVideoComposition * ) videoCompositionWithAsset
: ( AVAsset * ) assetapplyingCIFiltersWithHandler
: ( void
( ^ ) ( AVAsynchronousCIImageFilteringRequest * request
) ) applier
NS_AVAILABLE ( 10_11 , 9_0 ) ;
1.3.5.2 AVMutableVideoComposition
AVMutableVideoComposition 是 AVVideoComposition 的可变子类,它继承父类的属性可以改变,并且新增了下面的创建方法。
+ ( AVMutableVideoComposition * ) videoComposition
;
1.3.5.3 AVVideoCompositionInstruction
在上述的两个类中,真正包含有视频播放方式信息的是 instructions 属性,这个集合中的对象都遵循 AVVideoCompositionInstruction 协议,若不使用自定义的类,那么可以使用 AVFoundation 框架中的 AVVideoCompositionInstruction 类。 该类的相关属性如下:
@property
( nonatomic
, readonly
) CMTimeRange timeRange
;
@property
( nonatomic
, readonly
, retain
, nullable
) __attribute__ ( ( NSObject ) ) CGColorRef backgroundColor
;
@property
( nonatomic
, readonly
, copy
) NSArray < AVVideoCompositionLayerInstruction * > * layerInstructions
;
@property
( nonatomic
, readonly
) BOOL enablePostProcessing
;
@property
( nonatomic
, readonly
) NSArray < NSValue * > * requiredSourceTrackIDs
NS_AVAILABLE ( 10_9 , 7_0 ) ;
@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 是对给定的视频资源的不同播放方式进行描述的类,通过下面的方法,可以获取仿射变化、透明度变化、裁剪区域变化的梯度信息。
- ( BOOL ) getTransformRampForTime
: ( CMTime ) time startTransform
: ( nullable
CGAffineTransform * ) startTransform endTransform
: ( nullable
CGAffineTransform * ) endTransform timeRange
: ( nullable
CMTimeRange * ) timeRange
;
- ( BOOL ) getOpacityRampForTime
: ( CMTime ) time startOpacity
: ( nullable float
* ) startOpacity endOpacity
: ( nullable float
* ) endOpacity timeRange
: ( nullable
CMTimeRange * ) 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
+ ( 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
;
- ( void
) setTransform
: ( CGAffineTransform ) transform atTime
: ( CMTime ) time
;
- ( 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
在自定义视频播放时,可能需要添加水印、标题或者其他的动画效果,需要使用该类。该类通常用来协调离线视频中图层与动画图层的组合(如使用 AVAssetExportSession 和 AVAssetReader 、AVAssetReader 类导出视频文件或读取视频文件时),而若是在线实时的视频播放,应使用 AVSynchronizedLayer 类来同步视频的播放与动画的效果。
在使用该类时,注意动画在整个视频的时间线上均可以被修改,所以,动画的开始时间应该设置为 AVCoreAnimationBeginTimeAtZero ,这个值其实比 0 大,属性值 removedOnCompletion 应该置为 NO,以防当动画执行结束后被移除,并且不应使用与任何的 UIView 相关联的图层。
作为视频组合的后期处理工具类,主要方法如下:
+ ( instancetype
) videoCompositionCoreAnimationToolWithAdditionalLayer
: ( CALayer * ) layer asTrackID
: ( CMPersistentTrackID ) trackID
;
+ ( instancetype
) videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer
: ( CALayer * ) videoLayer inLayer
: ( CALayer * ) 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 的校验方法。
- ( BOOL ) isValidForAsset
: ( nullable
AVAsset * ) asset timeRange
: ( CMTimeRange ) timeRange validationDelegate
: ( nullable id
< AVVideoCompositionValidationHandling > ) validationDelegate
NS_AVAILABLE ( 10_8 , 5_0 ) ;
- ( BOOL ) videoComposition
: ( AVVideoComposition * ) videoComposition shouldContinueValidatingAfterFindingInvalidValueForKey
: ( NSString * ) key
NS_AVAILABLE ( 10_8 , 5_0 ) ;
- ( BOOL ) videoComposition
: ( AVVideoComposition * ) videoComposition shouldContinueValidatingAfterFindingEmptyTimeRange
: ( CMTimeRange ) timeRange
NS_AVAILABLE ( 10_8 , 5_0 ) ;
- ( BOOL ) videoComposition
: ( AVVideoComposition * ) videoComposition shouldContinueValidatingAfterFindingInvalidTimeRangeInInstruction
: ( id
< AVVideoCompositionInstruction > ) videoCompositionInstruction
NS_AVAILABLE ( 10_8 , 5_0 ) ;
- ( 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 之 视音频编辑
详情可以参考苹果官方文档:Editing章节 AVFoundation使用组合从现有的媒体片段(通常是一个或多个视频和音频轨道)创建新资产。您可以使用可变组合来添加和删除轨迹,并调整它们的时间顺序。你也可以设置音轨的相对音量和倾斜;设置视频轨迹的不透明度和不透明度坡道。合成是存储在内存中的媒体片段的集合。当您使用导出会话导出一个组合时,它会折叠成一个文件。您还可以使用资产写入器从媒体(例如示例缓冲区或静态图像)创建资产。 AVFoundation 框架中提供了丰富的接口用于视听资源的编辑,其中的关键是 composition ,它将不同的 asset 相结合并形成一个新的 asset 。使用 AVMutableComposition 类可以增删 asset 来将指定的 asset 集合到一起。除此之外,若想将集合到一起的视听资源以自定义的方式进行播放,需要使用 AVMutableAudioMix 和 AVMutableVideoComposition类对其中的资源进行协调管理。最终要使用 AVAssetExportSession 类将编辑的内容保存到文件中。 AVFoundation框架提供了一组功能丰富的类,以方便编辑视听资产。AVFoundation编辑API的核心是复合。组合就是来自一个或多个不同媒体资产的音轨集合。AVMutableComposition类提供了一个接口,用于插入和删除轨迹,以及管理它们的时间顺序。图3-1显示了如何将现有资产组合拼凑成新资产。如果您想做的只是将多个资产按顺序合并到一个文件中,那么这就是您所需要的全部细节。如果你想在你的作曲中对音轨进行任何自定义音频或视频处理,你需要分别合并一个音频混合或一个视频合成。
使用AVMutableAudioMix类,您可以在组合中的音频轨道上执行自定义音频处理,如图3-2所示。目前,您可以为音轨指定最大音量或设置音量斜坡。
您可以使用AVMutableVideoComposition类来直接编辑合成中的视频轨迹,如图3-3所示。对于单个视频合成,您可以为输出视频指定所需的渲染大小和比例以及帧持续时间。通过视频合成的指令(由AVMutableVideoCompositionInstruction类表示),您可以修改视频的背景颜色并应用层指令。这些层指令(由AVMutableVideoCompositionLayerInstruction类表示)可用于对组合中的视频轨道应用转换、转换坡道、不透明度和不透明度坡道。video composition类还允许您使用animationTool属性将核心动画框架的效果引入到视频中。
要将组合与音频和视频组合组合在一起,可以使用AVAssetExportSession对象,如图3-4所示。使用组合初始化导出会话,然后分别将音频混合和视频组合分配给audioMix和videoComposition属性。
1.4.1 AVAssetExportSession
使用 AVAssetExportSession 类对视频进行裁剪及转码,即将一个 AVAsset 类实例修改后保存为另一个 AVAsset 类实例,最后保存到文件中。 在修改资源之前,为避免不兼容带来的错误,可以先调用下面的方法,检查预设置是否合理。
+ ( NSArray < NSString * > * ) exportPresetsCompatibleWithAsset
: ( AVAsset * ) 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
: anAssetoutputFileType
: AVFileTypeMPEG4 completionHandler
: ^ ( BOOL compatible
) { if ( compatible
) { AVAssetExportSession * exportSession
= [ [ AVAssetExportSession alloc
] initWithAsset
: anAssetpresetName
: 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
@property
( nonatomic
, readonly
) NSArray < AVCompositionTrack * > * tracks
;
@property
( nonatomic
, readonly
) CGSize naturalSize
;
@property
( nonatomic
, readonly
, copy
) NSDictionary < NSString * , id
> * URLAssetInitializationOptions NS_AVAILABLE ( 10_11 , 9_0 ) ;
- ( nullable
AVCompositionTrack * ) trackWithTrackID
: ( CMPersistentTrackID ) trackID
;
- ( NSArray < AVCompositionTrack * > * ) tracksWithMediaType
: ( NSString * ) mediaType
;
- ( NSArray < AVCompositionTrack * > * ) tracksWithMediaCharacteristic
: ( NSString * ) mediaCharacteristic
;
值得注意的是 AVComposition 类中并没有提供初始化方法,一般我们使用它的子类 AVMutableComposition ,进行各种操作后,再生成 AVComposition 实例以供查询,如下:
AVMutableComposition * mutableComposition
= [ AVMutableComposition composition
] ;
< #····#
>
AVComposition * composition
= [ myMutableComposition copy
] ;
AVPlayerItem * playerItem
= [ [ AVPlayerItem alloc
] initWithAsset
: composition
] ;
1.4.3 AVMutableComposition
AVMutableComposition 是 AVComposition 的子类,其包含的 tracks 则是 AVCompositionTrack 的子类 AVMutableCompositionTrack 。 AVMutableComposition 中提供了两个类方法用来获取一个空的 AVMutableComposition 实例对象。
+ ( instancetype
) composition
;
+ ( instancetype
) compositionWithURLAssetInitializationOptions
: ( nullable
NSDictionary < NSString * , id
> * ) URLAssetInitializationOptions NS_AVAILABLE ( 10_11 , 9_0 ) ;
对整个 composition 中的 tracks 的修改方法如下:
- ( BOOL ) insertTimeRange
: ( CMTimeRange ) timeRange ofAsset
: ( AVAsset * ) asset atTime
: ( CMTime ) startTime error
: ( NSError * _Nullable
* _Nullable
) outError
;
- ( void
) insertEmptyTimeRange
: ( CMTimeRange ) timeRange
;
- ( void
) removeTimeRange
: ( CMTimeRange ) timeRange
;
- ( void
) scaleTimeRange
: ( CMTimeRange ) timeRange toDuration
: ( CMTime ) duration
;
从 composition 中获取 track 或向其中添加/移除 track 方法如下:
- ( AVMutableCompositionTrack * ) addMutableTrackWithMediaType
: ( NSString * ) mediaType preferredTrackID
: ( CMPersistentTrackID ) preferredTrackID
;
- ( void
) removeTrack
: ( AVCompositionTrack * ) track
;
- ( 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 中提供的属性如下:
@property
( nonatomic
) CMTimeScale naturalTimeScale
;
@property
( nonatomic
, copy
, nullable
) NSString * languageCode
;
@property
( nonatomic
, copy
, nullable
) NSString * extendedLanguageTag
;
@property
( nonatomic
) CGAffineTransform preferredTransform
;
@property
( nonatomic
) float preferredVolume
;
@property
( nonatomic
, copy
, null_resettable
) NSArray < AVCompositionTrackSegment * > * segments
;
当我们获取了一个 AVMutableCompositionTrack 实例对象后,便可以通过以下方法对其进行添加或移除数据片段:
- ( 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
;
- ( 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 有两个属性:
timeMapping 描述的是数据片段在整个媒体文件中所处的时间范围.timeMapping 是一个结构体,拥有两个成员,对于编辑中的媒体数据片段,它们分别表示数据在源文件中的位置和目标文件中的位置. empty 描述该数据片段是否为空,如果为空,其 timeMapping.source.start 为 kCMTimeInvalid
1.4.7 AVCompositionTrackSegment
在编辑媒体文件时,在描述数据时,使用的是 AVAssetTrackSegment 的子类 AVCompositionTrackSegment ,它的主要属性和方法如下:
@property
( nonatomic
, readonly
, getter
= isEmpty ) BOOL empty
;
@property
( nonatomic
, readonly
, nullable
) NSURL * sourceURL
;
@property
( nonatomic
, readonly
) CMPersistentTrackID sourceTrackID
;
+ ( 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 之 视音频媒体捕获
将现实世界的模拟信号转换为能够被计算机存储和传输的数字信号,需要经过模拟-数字转换过程,也称为采样。数字化采样有两种方式,一种是时间采样,即捕捉一个信号周期内的变化,一种是空间采样,一般用于图片数字化和其他可视化媒体内容数字化上,它对一个图片在一定分辨率下捕捉其亮度和色度,进而创建出由该图片的像素点数据所构成的数字化结果。视频既有空间属性又有时间属性,所以数字化时都可以使用。
对于一个音频,麦克风负责将物理振动转化为相同频率和振幅的电流信号,之后通过线性脉冲编码调制(LPCM)进行编码。LPCM 采样或测量一个固定的音频信号,其周期率被称为采样率。采样频率达到奈奎斯特频率(采样对象最高频率的 2 倍)时即可准确表现原始信号的信息。另一方面编码字节数(也称位元深度)表现了采样精度,如位元深度为 8 位的编码能够提供 256 个离散级别的数据,CD 音质位元深度为 16
摄像机和麦克风的记录输入由捕获会话管理。捕获会话协调从输入设备到输出(如电影文件)的数据流。您可以为单个会话配置多个输入和输出,甚至在会话运行时也是如此。向会话发送消息以启动和停止数据流。此外,您可以使用预览层的实例向用户显示摄像机正在录制的内容。
更多关于视频捕获的详情可以参考苹果官方文档:Still and Video Media Capture章节
要管理来自摄像机或麦克风等设备的捕获,您需要组装对象来表示输入和输出,并使用AVCaptureSession实例来协调它们之间的数据流。你需要最低限度:
表示输入设备的AVCaptureDevice实例,如摄像机或麦克风 AVCaptureInput的一个具体子类的实例,用于配置来自输入设备的端口 AVCaptureOutput的一个具体子类的实例,用于管理电影文件或静态图像的输出 AVCaptureSession的一个实例,用于协调从输入到输出的数据流
要向用户显示摄像机记录内容的预览,可以使用AVCaptureVideoPreviewLayer的一个实例(CALayer的一个子类)。
您可以配置多个输入和输出,由单个会话进行协调,如图4-1所示:
对于许多应用程序,这是您需要的尽可能多的细节。但是,对于某些操作(例如,如果希望监视音频通道中的功率级别),需要考虑如何表示输入设备的各个端口,以及如何将这些端口连接到输出。
捕获会话中的捕获输入和捕获输出之间的连接由AVCaptureConnection对象表示。捕获输入(AVCaptureInput的实例)有一个或多个输入端口(AVCaptureInputPort的实例)。捕获输出(AVCaptureOutput的实例)可以接受来自一个或多个源的数据(例如,AVCaptureMovieFileOutput对象同时接受视频和音频数据)。
当您将输入或输出添加到会话时,会话将在所有兼容的捕获输入端口和捕获输出之间形成连接,如图4-2所示。捕获输入和捕获输出之间的连接由AVCaptureConnection对象表示。
您可以使用捕获连接来启用或禁用来自给定输入或到给定输出的数据流。您还可以使用连接来监视音频通道中的平均和峰值功率级别。
通过麦克风、摄像机等设备,可以捕获外界的声音和影像。要处理设备捕获的数据,需要使用 AVCaptureDevice 类描述设备,使用 AVCaptureInput 配置数据从设备的输入,使用 AVCaptureOutput 类管理数据到文件的写入,而数据的输入到写出,需要使用 AVCaptureSession 类进行协调。此外,可以使用 AVCaptureVideoPreviewLayer 类显示相机正在拍摄的画面。
一个设备可以有多个输入,使用 AVCaptureInputPort 类描述这些输入,用 AVCaptureConnection 类描述具体类型的输入与输出的关系,可以实现更精细的数据处理。
1.5.1 AVCaptureSession
`AVCaptureSession 是捕获视听数据的核心类,它协调数据的输入和输出。创建一个 AVCaptureSession 类的对象时,可以指定最终得到的视听数据的质量,当然这个质量与设备也有关系,通常在设置之前,可以调用方法判断 session 是否支持要设置的质量。
AVCaptureSession 类实例可设置的数据质量有 AVCaptureSessionPresetHigh 、AVCaptureSessionPresetMedium 、AVCaptureSessionPresetLow 、AVCaptureSessionPreset320x240 等。在进行设置之前,可以调用 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 开始执行、结束执行、执行过程中出错或被打断时,都会发出通知,通过注册下面的通知,可以获取我们感兴趣的信息。
VCaptureSessionRuntimeErrorNotification 通过 AVCaptureSessionErrorKey 可以获取出错的原因 AVCaptureSessionDidStartRunningNotification 开始 session AVCaptureSessionDidStopRunningNotification 结束 session AVCaptureSessionWasInterruptedNotification 通过 AVCaptureSessionInterruptionReasonKey 可以获取被打断的原因 AVCaptureSessionInterruptionEndedNotification 打断结束,session 重新开始
1.5.2 AVCaptureDevice
AVCaptureDevice是用来描述设备属性的类,要捕获视听数据,需要获取相应的设备,使用该类获取有效的设备资源。这个设备资源列表是随时变动的,其在变动时,会发送AVCaptureDeviceWasConnectedNotification 或 AVCaptureDeviceWasDisconnectedNotification 通知,以告知有设备连接或断开。
在获取设备之前,要先确定要获取的设备的类型 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
;
获取一个设备后,可以通过修改它的属性来满足自己的需要。
flashMode 闪光灯的模式(AVCaptureFlashModeOff 、AVCaptureFlashModeOn 、AVCaptureFlashModeAuto) torchMode 手电筒的模式(AVCaptureTorchModeOff 、AVCaptureTorchModeOn 、AVCaptureTorchModeAuto) torchLevel 手电筒的亮度(0~1) focusMode 聚焦模式(AVCaptureFocusModeLocked 、AVCaptureFocusModeAutoFocus 、AVCaptureFocusModeContinuousAutoFocus) exposureMode 曝光模式(AVCaptureExposureModeLocked 、AVCaptureExposureModeAutoExpose 、AVCaptureExposureModeContinuousAutoExposure 、AVCaptureExposureModeCustom) whiteBalanceMode 白平衡模式(AVCaptureWhiteBalanceModeLocked 、AVCaptureWhiteBalanceModeAutoWhiteBalance 、AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance)
在修改这些属性时,应先判断当前设备是否支持要设置的属性值,并且所有的属性修改都要放在下面两个方法之间,以保证属性能够被正确设置。
- ( BOOL ) lockForConfiguration
: ( NSError * * ) outError
;
- ( void
) unlockForConfiguration
;
在调用硬件设备之前,应先判断应用是否拥有相应的权限,其权限分为以下几种:
AVAuthorizationStatusNotDetermined 未定义 AVAuthorizationStatusRestricted 无权限(因某些原因,系统拒绝权限) AVAuthorizationStatusDenied 无权限(用户拒绝) AVAuthorizationStatusAuthorized 有权限
+ ( AVAuthorizationStatus ) authorizationStatusForMediaType
: ( NSString * ) mediaType
NS_AVAILABLE_IOS ( 7_0 ) ;
+ ( void
) requestAccessForMediaType
: ( NSString * ) mediaType completionHandler
: ( void
( ^ ) ( BOOL granted
) ) handler
NS_AVAILABLE_IOS ( 7_0 ) ;
1.5.3 AVCaptureDeviceInput
AVCaptureDeviceInput 是 AVCaptureInput 的子类,使用一个 AVCaptureDevice 类实例创建该类的实例,其管理设备的输入。 在创建了实例对象后,将其添加到 session 中。
NSError * error
;
AVCaptureDeviceInput * input
= [ AVCaptureDeviceInput deviceInputWithDevice
: device error
: & error
] ;
if ( input
&& [ session canAddInput
: input
] ) { [ captureSession addInput
: captureDeviceInput
] ;
}
1.5.4 AVCaptureOutput
AVCaptureOutput 是一个抽象类,通常使用的是它的子类
AVCaptureMovieFileOutput 用来生成一个影视文件 AVCaptureVideoDataOutput 用来处理输入的视频的帧 AVCaptureAudioDataOutput 用来处理音频数据 AVCaptureStillImageOutput 用来获取图片
在创建了具体的子类后,将它添加到 session 中
AVCaptureMovieFileOutput * movieOutput
= [ [ AVCaptureMovieFileOutput alloc
] init ] ;
if ( [ session canAddOutput
: movieOutput
] ) { [ session addOutput
: movieOutput
] ;
}
1.5.5 AVCaptureFileOutput
AVCaptureFileOutput 是 AVCaptureOutput 的子类,是 AVCaptureMovieFileOutput 、AVCaptureAudioFileOutput 的父类。这个类中定义了文件输出时的地址、时长、容量等属性。
@
property ( nonatomic
, readonly
) NSURL * 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
;
@
property ( nonatomic
) CMTime maxRecordedDuration
;
@
property ( nonatomic
) int64_t maxRecordedFileSize
;
@
property ( nonatomic
) int64_t minFreeDiskSpaceLimit
;
@
property ( nonatomic
, assign
) id
< AVCaptureFileOutputDelegate > delegate
NS_AVAILABLE ( 10_7 , NA ) ;
@
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 是文件记录过程中需要用到的协议,它通常的作用是告知代理对象文件记录结束了。
- ( void
) captureOutput
: ( AVCaptureFileOutput * ) captureOutput didFinishRecordingToOutputFileAtURL
: ( NSURL * ) outputFileURL fromConnections
: ( NSArray * ) connections error
: ( NSError * ) error
;
- ( void
) captureOutput
: ( AVCaptureFileOutput * ) captureOutput didStartRecordingToOutputFileAtURL
: ( NSURL * ) fileURL fromConnections
: ( NSArray * ) connections
;
- ( 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 ) ;
- ( 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 系统下,它给了客户端精准操控数据的机会。
- ( BOOL ) captureOutputShouldProvideSampleAccurateRecordingStart
: ( AVCaptureOutput * ) captureOutput
NS_AVAILABLE ( 10_8 , NA ) ;
- ( 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
;
@
property ( nonatomic
, copy
) NSArray * metadata
;
@
property ( nonatomic
, copy
) NSDictionary * audioSettings
;
1.5.9 AVCaptureVideoDataOutput
AVCaptureVideoDataOutput 是 AVCaptureOutput 的子类,该类可以用来处理捕获的每一个视频帧数据。创建一个该类的实例对象后,要调用下面的方法设置一个代理对象,及调用代理对象所实现的协议方法的队列。
- ( void
) setSampleBufferDelegate
: ( id
< AVCaptureVideoDataOutputSampleBufferDelegate > ) sampleBufferDelegate queue
: ( dispatch_queue_t
) sampleBufferCallbackQueue
;
指定的队列 sampleBufferCallbackQueue 必需是串行队列以保证传递的帧是按记录时间先后传递的。
@
property ( nonatomic
, copy
) NSDictionary * videoSettings
;
@
property ( nonatomic
, readonly
) NSArray * availableVideoCVPixelFormatTypes
NS_AVAILABLE ( 10_7 , 5_0 ) ;
@
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
;
- ( void
) captureOutput
: ( AVCaptureOutput * ) captureOutput didDropSampleBuffer
: ( CMSampleBufferRef ) sampleBuffer fromConnection
: ( AVCaptureConnection * ) connection
NS_AVAILABLE ( 10_7 , 6_0 ) ;
1.5.11 AVCaptureVideoPreviewLayer
AVCaptureVideoPreviewLayer 是 CALayer 的子类,使用该类可以实现捕获视频的显示。使用一个 session 创建一个该类对象,而后将该类对象插入到图层树中,从而显示捕获的视频。
+ ( instancetype
) layerWithSession
: ( AVCaptureSession * ) session
;
- ( instancetype
) initWithSession
: ( AVCaptureSession * ) session
;
修改 AVCaptureVideoPreviewLayer 的属性 videoGravity 值,可以选择显示捕获视频时的界面大小变化方式,它有以下可选值:
AVLayerVideoGravityResize 默认值,直接铺满屏幕,及时画面变形 AVLayerVideoGravityResizeAspect 保持画面的横纵比,不铺满屏幕,多余的空间显示黑色 AVLayerVideoGravityResizeAspectFill 保持画面的横纵比,铺满屏幕,多余的画面进行裁剪
1.5.12 AVCaptureAudioDataOutput
AVCaptureAudioDataOutput 是 AVCaptureOutput 的子类,该类可以处理接收到的音频数据。同 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 对象,而后再调用下面的方法,来获取一张或多张图片。
- ( nullable
CGImageRef ) copyCGImageAtTime
: ( CMTime ) requestedTime actualTime
: ( nullable
CMTime * ) actualTime error
: ( NSError * _Nullable
* _Nullable
) outError
CF_RETURNS_RETAINED ;
- ( void
) generateCGImagesAsynchronouslyForTimes
: ( NSArray < NSValue * > * ) requestedTimes completionHandler
: ( AVAssetImageGeneratorCompletionHandler ) handler
;
typedef void
( ^ AVAssetImageGeneratorCompletionHandler ) ( CMTime requestedTime
, CGImageRef _Nullable image
, CMTime actualTime
, AVAssetImageGeneratorResult result
, NSError * _Nullable error
) ;
1.6 AVFoundation 之媒体流输出
对媒体数据资源进行简单的转码或裁剪,使用 AVAssetExportSession 类便足够了,但是更深层次的修改媒体资源,便需要用到 AVAssetReader 类和 AVAssetWriter 类。 AVAssetReader 只能与一个资源 asset 相关联,且不能用来读取实时数据,在开始读取数据之前,需要为 reader 添加 AVAssetReaderOutput 的实例对象。这个实例对象描述的是待读取的数据资源来源类型,通常使用 AVAssetReaderAudioMixOutput 、AVAssetReaderTrackOutput 、AVAssetReaderVideoCompositionOutput 三种子类。 AVAssetWriter 可以将来自多个数据源的数据以指定的格式写入到一个指定的文件中,且其只能对应一个文件。在写文件之前,需要用每一个 AVAssetWriterInput 类实例对象来描述相应的数据源。每一个 AVAssetWriterInput 实例对象接收的数据都应是 CMSampleBufferRef 类型的变量。如果使用 AVAssetWriterInputPixelBufferAdaptor 类也可以直接将 CVPixelBufferRef 类型的变量数据添加到 writer input 中。 AVAssetReader 与 AVAssetWriter 结合起来使用,便可以对读取的数据进行相应的编辑修改,而后写入到一个文件中并保存。
1.6.1 AVAssetReader
使用该类读取媒体资源,其提供的初始化方法与一个 asset 相关联。
+ ( nullable instancetype
) assetReaderWithAsset
: ( AVAsset * ) asset error
: ( NSError * _Nullable
* _Nullable
) outError
;
- ( nullable instancetype
) initWithAsset
: ( AVAsset * ) asset error
: ( NSError * _Nullable
* _Nullable
) outError
NS_DESIGNATED_INITIALIZER ;
AVAssetReaderStatusCompleted 、
AVAssetReaderStatusFailed 、
AVAssetReaderStatusCancelled
@property
( readonly
) AVAssetReaderStatus status
;
@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
;
@property
( nonatomic
) BOOL alwaysCopiesSampleData
NS_AVAILABLE ( 10_8 , 5_0 ) ;
- ( nullable
CMSampleBufferRef ) copyNextSampleBuffer
CF_RETURNS_RETAINED ;
@property
( nonatomic
) BOOL supportsRandomAccess
NS_AVAILABLE ( 10_10 , 8_0 ) ;
- ( void
) resetForReadingTimeRanges
: ( NSArray < NSValue * > * ) timeRanges
NS_AVAILABLE ( 10_10 , 8_0 ) ;
- ( void
) markConfigurationAsFinal
NS_AVAILABLE ( 10_10 , 8_0 ) ;
1.6.3 AVAssetReaderTrackOutput
AVAssetReaderTrackOutput 是 AVAssetReaderOutput 的子类,它用来描述待读取的数据来自 asset track ,在读取前,还可以对数据的格式进行修改。
+ ( 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
AVAssetReaderAudioMixOutput 是 AVAssetReaderOutput 的子类,它用来描述待读取的数据来自音频组合数据。创建该类实例对象提供的参数 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
AVAssetReaderVideoCompositionOutput 是 AVAssetReaderOutput 的子类,该类用来表示要读取的类是组合的视频数据。 同 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 之媒体的时间和数据
总结
以上是生活随笔 为你收集整理的IOS音视频(一)AVFoundation核心类 的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔 网站内容还不错,欢迎将生活随笔 推荐给好友。