短视频编辑中的AVFoundation框架(一)框架概述与基础

2,616 阅读18分钟

前言

提到短视频编辑,我们可能会第一时间想到FFmpegOpenGL ES这些让人望而生畏的庞大框架,诚然音视频开发的入门的确需要一定的基础,但是仅仅依托于苹果为我们提供的AVFoundation框架,我们仍然可以在短视频编辑领域做很多事情。本系列文章围绕iOS平台上短视频编辑功能中AVFoundation扮演的角色展开讨论,本篇主要介绍学习AVFoundation框架需要了解的基础模块和相关的数据类型及注意事项。

一、 AVFouondation框架概述

AVFoundation 是在 iOS、macOS、watchOS 和 tvOS 上处理多媒体数据的功能齐全的框架。使用AVFoundation,我们可以播放、创建和编辑 QuickTime movie和 MPEG-4 文件,播放 HLS 流,并将强大的媒体编辑功能构建到我们的应用程序中。

1.1 iOS多媒体框架体系

我们首先来看AVFouondation框架在苹果的多媒体框架体系中的位置。在iOS的多媒体体系中,高层级的AVKit提供了 高度封装的播放器控制类AVPlayerViewController 、用于切换播放路由(投屏)的AVRoutePickerView,以及实现画中画播放效果的AVPictureInPictureController。低层级的框架主要以C接口为主,其中:

  • Core Audio 最底层的音频处理接口,直接驱动设备的音频硬件,对音乐游戏或专业的音频编辑软件提供了全面的支持。Audio Unit提供了包含合成乐器声音、回声消除、混音、声音均衡等相关接口。

Audio Unit已经迁移到 Audio Toolbox Framework了。

  • Core Video 为其相对的Core Media提供图片缓存(CVPixelBuffer)和缓存池(CVPixelBufferPool)支持,提供了一个能够对数字视频逐帧访问的接口,以及对Metal(CVMetalTexture) 、OpenGL(CVOpenGLTexture) 以及OpenGLES(CVOpenGLESTexture)的支持。
  • Core Media 定义和封装了AVFoundation等更上层的媒体框架需要的媒体处理流水线(包含时间信息)以及其中使用的接口和数据类型(CMSampleBufferCMTime)。使用 Core Media 层的接口和数据类型可以高效的处理媒体采样数据、管理采样数据队列(CMSimpleQueueCMBufferQueue)。
  • Core Animation 是iOS中动画相关的框架,AVFoundation结合Core Animation让开发者能够在视频编辑和播放过程中添加动画和贴纸效果。

1-1

而AVFoundation位于高层级框架和低层级框架之间,封装了低层级框架才能实现的功能,提供了OC和Swift语言的接口,同时苹果在迭代过程中不断优化AVFoundation这种中间层框架的性能,很好地支持了新的设备和视频格式。

1.2 AVFoundation各模块简介

AVFoundation官方文档介绍:AVFoundation框架结合了六个主要技术领域,这些领域覆盖了在Apple平台上录制、处理、合成、控制、导入和导出视听媒体的主要功能。而API官方文档将AVFoundation分成了Assets媒体资源、Playback播放、Capture录制、Editing编辑、Audio音频、Speech(语音播报)六个功能模块。

1-2

  • Assets:提供加载、检查和导出媒体资源和元数据信息的功能,也可以使用AVAssetReaderAVAssetWriter对媒体样本数据进行样本级读写,使用AVAssetImageGenerator获取视频缩略图,使用AVCaption进行字幕创作(mac os)等。
  • Playback:提供对AVAsset的播放和播放控制的功能,可以使用AVPlayer播放一个项目,也可以使用AVQueuePlayer播放多个项目,AVSynchronizedLayer可以让我们结合Core Animation将动画层与播放视图层进行同步,实现播放中的诸如贴纸、文字等效果。
  • Capture:用于拍摄照片、录制音频和视频,通过配置内置摄像头和麦克风或外部录制设备,可以构建自定义相机功能,控制照片和视频拍摄的输出格式,或者直接修改音视频数据流作为自定义输出。
  • Editing:用于将来自多个来源的音频和视频轨道组合、编辑和重新混合到一个AVMutableComposition中,可以使用AVAudioMixAVVideoComposition分别控制音频混合和视频合成的细节。
  • Audio:播放、录制和处理音频;配置应用程序的系统音频行为。苹果又在iOS14.5整合了一个AVFAudio框架,内容与这部分完全相同,可能未来会把音频部分单独处理。
  • Speech:将文本转换为语音音频进行朗读。

在短视频编辑中,无论是编辑所用的素材还是编辑中处理的半成品亦或是最终导出的成品,我们都在跟AVAsset或其子类打交道,其所在的Assets模块作为AVFoundation媒体处理的基础是需要首先学习的内容。

二、 基础模块-Assets

2.1 AVAsset & AVAssetTrack

AVAsset是一个抽象类和不可变类,定义了媒体资源混合呈现的方式,将媒体资源的静态属性模块化成一个整体。它提供了对基本媒体格式的抽象,这意味着无论是处理Quick Time Movie还是MP3音频,对开发者和对框架其余部分而言,面对的只有资源这个概念。

AVAsset通常通过其子类AVURLAsset实例化,其中的参数"URL"可以来自远程或者本地甚至是流媒体,使我们无须关注其来源,只须专注于处理AVAsset本身。

使用下面代码示例中的方法创建返回的都是AVURLAsset的实例。方法二中的options可以用来来自定义AVURLAsset的初始化以满足特定需求。例如,通过HLS流创建AVURLAsset,使用{AVURLAssetAllowsCellularAccessKey: @NO}作为options的参数,可以阻止在使用蜂窝网络时检索其媒体数据。而与视频编辑密切相关的是AVURLAssetPreferPreciseDurationAndTimingKey,用于指示资源是否提供准确的"duration"值和精确的随机访问,视频编辑需要精确的值,建议使用"YES",不过这种精度可能需要进行额外的解析而导致更长的加载时间。

许多容器格式为精确计时提供了足够的摘要信息,并不需要额外的解析来准备,例如QuickTime Movie和MPEG-4文件。

AVAsset *asset = [AVAsset assetWithURL:url];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetPreferPreciseDurationAndTimingKey: @YES}];

一个AVAsset实例是一个或多个AVAssetTrack实例的容器,该实例是对媒体的统一类型"轨道"进行的建模。一个简单的视频文件通常包含一个音频轨道和一个视频轨道,也可能包含一些补充内容,如隐藏式字幕、字幕,也可能包含描述媒体内容的元数据(AVMetadataItem)。

隐藏式字幕即“Closed Caption”,简称CC字幕。大多数CC字幕和剧本是一样的,里面除了对白之外,还有场景中产生的声音和配乐等的描述,主要是为了听障人士所设置的,“Closed”一词也说明了并不是默认开启的状态,与之相对的是“Open Caption”,也就是通常所说的字幕,而与对话语言相同的字幕称为”Caption“,不同的(即翻译)称为“Subtitle”。

创建一个AVAsset是一个轻量级操作,因为AVAsset的底层媒体数据采用了延迟加载的设计,直到获取时才会进行加载,并且属性的获取是同步进行的,如果没有进行提前进行异步加载直接去访问它的属性会阻塞调用线程,不过这也要取决于要访问的媒体数据的大小和位置。为了避免阻塞线程,我们最好在使用之前进行异步加载属性。AVAsset和AVAssetTrack都遵循了AVAsynchronousKeyValueLoading协议,可以进行异步加载属性和获取加载状态。

@protocol AVAsynchronousKeyValueLoading
// 异步加载包含在keys数组中的属性,在handler中使用statusOfValueForKey:error:方法判断加载是否完成。
- (void)loadValuesAsynchronouslyForKeys:(NSArray<NSString *> *)keys completionHandler:(nullable void (^)(void))handler;
// 获取key属性加载的状态,status为AVKeyValueStatusLoaded为加载完成。
- (AVKeyValueStatus)statusOfValueForKey:(NSString *)key error:(NSError * _Nullable * _Nullable)outError;

WWDC2021What’s new in AVFoundation提到,针对swift API引入了async / await ,让我们得以使用与同步编程类似的控制流来进行异步编程。

let asset = AVAsset (url: assetURL)
let duration = trv await asset.load(.duration)
// 我们也可以加载多个属性,使用元组接收返回值:
let (duration, tracks) = try await asset.load(.duration, .tracks)

AVAsset的属性:

代码示例中tracks属性返回的是一个AVAsset实例包含的所有AVAssetTrack实例的数组,苹果也提供了根据特定标准(如轨道ID、媒体类型和特征)检索轨道子集的方法如下,这也是编辑模块中取出某种类型的轨道常用的方法。

// 根据TrackID检索轨道
- (void)loadTrackWithTrackID:(CMPersistentTrackID)trackID completionHandler:(void (^)(AVAssetTrack * _Nullable_result, NSError * _Nullable))completionHandler;
// 根据媒体类型检索轨道子集
- (voidloadTracksWithMediaType:(AVMediaType)mediaType completionHandler:(void (^)(NSArray<AVAssetTrack *> * _Nullable NSError * _Nullable))completionHandler;
// 根据媒体特征检索轨道子集
- (void)loadTracksWithMediaCharacteristic:(AVMediaCharacteristic)mediaCharacteristic completionHandler:(void (^)(NSArray<AVAssetTrack *> * _Nullable, NSError * _Nullable))completionHandler;

其中AVMediaType常用的有:音频AVMediaTypeAudio、视频AVMediaTypeVideo、字幕AVMediaTypeSubtitle、元数据AVMediaTypeMetadata等。

AVMediaCharacteristic用于定义媒体数据特征,例如是否包含HDR视频轨道AVMediaCharacteristicContainsHDRVideo,是否包含可听内容AVMediaCharacteristicAudible等。

2.2 元数据

媒体容器格式存储关于其媒体的描述性元数据,每种容器格式都有自己独特的元数据格式,AVFoundation通过使用其AVMetadataItem类简化了对元数据的处理,在最基本的形式中,AVMetadataItem的实例是一个键值对,表示单个元数据值,比如电影的标题或专辑的插图。

要高效地使用AVMetadataItem,我们需要了解AVFoundation是如何组织元数据的。为了简化元数据项的查找和过滤,AVFoundation框架将相关元数据分组到键空间中:

  • 特定格式的键空间。AVFoundation框架定义了几个特定格式的键空间,它们大致与特定容器或文件格式相关,例如 QuickTime(QuickTime 元数据和用户数据)或 MP3 (ID3)。但是,单个资源可能包含跨多个键空间的元数据值。要检索资源的特定格式元数据的完整集合,可以使用metadata属性。
  • Common键空间。有几个常见的元数据值,例如电影的创建日期或描述,可以存在于多个键空间中。为了帮助规范化对这个公共元数据的访问,该框架提供了一个common键空间,它允许访问几个键空间共有的一组有限元数据值。要检索资源的公共元数据集合,可以直接使用commonMetadata属性。

除此之外,我们还可以通过调用AVAssetavailableMetadataFormats属性来确定资源包含哪些元数据格式。此属性返回包含每个元数据格式的字符串标识符数组。然后使用它的metadataForFormat:方法,通过传递适当的格式标识符来检索特定于格式的元数据值。

一个iPhone13 Pro拍摄的HDR视频文件的元数据:

creationDate :  2022-03-01T18:16:17+0800
location : +39.9950+116.4749+044.903/
make: Apple
model:  iPhone 13 Pro
software: 15.3.1

虽然本系列文章旨在不过多关注音视频编解码格式,但是拿到一个视频文件(.mov),如果仍然想获取视频样本的编码类型(h264/hevc)、转换函数(ITU_R_709_2/ITU_R_2100_HLG)等,获取音频样本的采样率、通道数、位深等格式信息,我们应该从哪里入手呢?前面我们介绍了在一个AVAsset资源中以轨道(track)的形式把音频、视频等数据分别进行了单独的建模,如果要获取视频样本格式的信息,只要根据媒体类型检索相应的视频轨道,获取assetTrack的formatDescriptions属性,即可拿到视频样本的格式信息CMVideoFormatDescription的集合,同样还有CMAudioFormatDescriptionCMClosedCaptionFormatDescription等用于描述各自轨道样本的数据格式。

// 获取数据样本格式信息
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
NSArray *videoFormats = VideoTrack.formatDescriptions;

上述视频文件中的视频轨道格式描述信息如下:

"<CMVideoFormatDescription 0x2834305a0 [0x1dbce41b8]> {
	mediaType:'vide' 
	mediaSubType:'hvc1' 
	mediaSpecific: {
        codecType: 'hvc1'		
        dimensions: 1920 x 1080 
	} 
	extensions: {{
        AmbientViewingEnvironment = {length = 8, bytes = 0x002fe9a03d134042};
        BitsPerComponent = 10;
        CVFieldCount = 1;
        CVImageBufferChromaLocationBottomField = Left;
        CVImageBufferChromaLocationTopField = Left;
        CVImageBufferColorPrimaries = \"ITU_R_2020\";
        CVImageBufferTransferFunction = \"ITU_R_2100_HLG\";
        CVImageBufferYCbCrMatrix = \"ITU_R_2020\";
        Depth = 24;
        FormatName = HEVC;
        FullRangeVideo = 0;
        RevisionLevel = 0;
        SampleDescriptionExtensionAtoms =     {
        dvvC = {
            length = 24, 
            bytes = 0x010010254000000000000000000000000000000000000000
            };
        hvcC = {
            length = 125, 
            bytes = 0x01022000 0000b000 00000000 78f000fc ... 2fe9a03d 13404280 };
    };
        SpatialQuality = 512;
        TemporalQuality = 512;
        VerbatimSampleDescription = {
            length = 289, 
            bytes = 0x00000121 68766331 00000000 00000001 ... 3d134042 00000000 };
        Version = 0;
}}
}"

2.3 视频预览

在短视频编辑中将播放导出前的视频资源称为预览,实际上播放属于AVFoundation中Playback模块的内容,但是本系列文章重点不在播放器上,我们简单介绍一下预览AVAsset使用的类AVPlayer

AVPlayer的初始化需要一个AVPlayerItem对象,它用来管理资源对象,提供播放数据源的类,要播放一个视频,只使用AVPlayer的话,只有声音没有画面,要显示画面我们还需要AVPlayerLayer

// 1.实例化AVAsset
AVAsset *asset = [AVAsset assetWithURL:url];

// 2.通过asset创建AVPlayerItem
AVPlayerItem* item = [[AVPlayerItem alloc] initWithAsset:asset];

// 3.创建AVPlayer
AVPlayer* player = [AVPlayer playerWithPlayerItem:item];

// 4.创建AVPlayerLayer用来显示视频
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];

// 5.将AVPlayerLayer添加到视图层级中
[self.view.layer addSublayer:playerLayer];

// 6.播放
[player play];

2.4 获取视频缩略图

通常在视频导出前会有一个选择视频封面的功能,该功能需要提供视频缩略图的列表,要获取视频的缩略图需要使用AVAssetImageGenerator,使用AVAsset实例创建 AVAssetImageGenerator对象的方法如下:

+ (instancetype)assetImageGeneratorWithAsset:(AVAsset *)asset;
- (instancetype)initWithAsset:(AVAsset *)asset;

如果需要精确时间截图,可以使用按照如下方法将前后时间容忍度设为kCMTimeZero,设置maximumSize属性可以指定截取画面的大小。

// 精确时刻获取缩略图
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
// 指定缩略图画面的大小
imageGenerator.maximumSize = CGSizeMake(100, 100);

然后调用如下方法来获取某个时刻的缩略图,或者多个时刻的缩略图:

// 获取指定时刻requestedTime的视频缩略图
- (nullable CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(nullable CMTime *)actualTime error:(NSError * _Nullable * _Nullable)outError;

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

// 上述方法的handler的类型定义,actualTime为缩略图所处的真实时刻
typedef void (^AVAssetImageGeneratorCompletionHandler)(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error);

三、 常用数据类型

3.1 CMTime & CMTimeRange

在获取视频缩略图中我们需要传入一个用于表示某个时刻的类型CMTime,通常我们认为用于表示时间的一般是NSTimeInterval(double类型),在AVFoundation中的AVAudioPlayerAVAudioRecorder处理时间可以使用,但是浮点型的不精确性(简单的舍入就会导致丢帧),无法用于更多的高级时基媒体的开发中,所以Core Media提供了这样一个结构体来表示时间:

typedef struct
{
   CMTimeValue    value;        
   CMTimeScale    timescale;
   CMTimeFlags    flags;
   CMTimeEpoch    epoch;
} CMTime;

其中CMTimeFlags是一个位掩码用于表示时间的指定状态,例如判断数据是否有效,CMTimeEpoch表示纪元,通常是0。我们重点关注表示时间的CMTimeValue和CMTimeScale,一个CMTime表示的时间 = value / timescale,timescale表示将时间划分了多少份,value表示该时间含有多少份,为满足大部分常用的视频频率24FPS、25FPS、30FPS,通常我们会将timescale设为他们的公倍数600。

我们可以使用如下方法创建一个CMTime:

CMTime time1 = CMTimeMake(3, 1); // 3 / 1 = 3s
CMTime time2 = CMTimeMakeWithSeconds(5, 1); 5s timescale = 1
NSDictionary *timeData = @{(id)kCMTimeValueKey : @2,
                           (id)kCMTimeScaleKey : @1,
                           (id)kCMTimeFlagsKey : @1,
                           (id)kCMTimeEpochKey : @0};
CMTime time3 = CMTimeMakeFromDictionary((__bridge CFDictionaryRef)timeData); 
// 特殊值
// 表示0时刻
CMTime time4 = kCMTimeZero; 
// 表示时间非法
CMTime time5 = kCMTimeInvalid; 

CMTime的运算:

// 加
CMTimeAdd(<#CMTime lhs#>, <#CMTime rhs#>)
// 减
CMTimeSubtract(<#CMTime lhs#>, <#CMTime rhs#>)
// 比较
CMTimeCompare(<#CMTime time1#>, <#CMTime time2#>)
// 校验
CMTIME_IS_VALID(<#time#>)
CMTIME_IS_INVALID(<#time#>)
// print
CMTimeShow(<#CMTime time#>)

CMTimeRange用于表示一个时间范围,由两个CMTime值组成,第一个定义时间范围的开始时间,第二个定义时间范围的持续时间。

typedef struct
{
    CMTime          start;
    CMTime          duration;
} CMTimeRange;

我们可以使用如下方法创建一个CMTimeRange:

CMTime beginTime = CMTimeMake(5, 1);
CMTime endTime = CMTimeMake(12, 1);
CMTimeRange timeRange1 = CMTimeRangeMake(beginTime, endTime);
CMTimeRange timeRange2 = CMTimeRangeFromTimeToTime(beginTime, endTime);
// 特殊值
// 表示0长度的时间范围
CMTimeRange timeRange3 = kCMTimeRangeZero;
// 表示时间范围非法
CMTimeRange timeRange4 = kCMTimeRangeInvalid;

CMTimeRange的运算:

// 取两个CMTimeRange的交集
CMTimeRangeGetIntersection(<#CMTimeRange range#>, <#CMTimeRange otherRange#>)
// 取两个CMTimeRange的并集
CMTimeRangeGetUnion(<#CMTimeRange range#>, <#CMTimeRange otherRange#>)
// 是否包含某个时刻CMTime
CMTimeRangeContainsTime(<#CMTimeRange range#>, <#CMTime time#>)
// 是否包含某个时间范围
CMTimeRangeContainsTimeRange(<#CMTimeRange range#>, <#CMTimeRange otherRange#>)
// 校验
CMTIMERANGE_IS_VALID(<#range#>)
CMTIMERANGE_IS_INVALID(<#range#>)
// print
CMTimeRangeShow(<#CMTimeRange range#>)

3.2 CMSampleBuffer

使用AVFoundation处理样本级的数据时要经常跟CMSampleBuffer打交道,例如AVFoundation的Capture模块相机采集的数据输出类型、AVAssetReaderAVAssetWriter读取和写入过程中操作的数据类型等。

CMSampleBuffer同样来自Core Media框架,它是系统用来通过媒体管道移动媒体样本数据的核心基础对象,CMSampleBuffer的角色是将基础的样本数据进行封装并提供格式描述时间信息

官方的文档介绍,它包含0个或多个特定媒体类型(音频、视频、混合等)的压缩(或未压缩)样本,CMSampleBuffer可以包含:

  • 样本数据。包含以下两者中的一个:

    • 一个或多个媒体样本的CMBlockBufferCMBlockBuffer为编码的数据,并未进行解码。
    • 一个或多个媒体样本的CVPixelBufferCVPixelBuffer为编码前或解码后的数据。
  • 时间信息。CMSampleBuffer还包含表示当前样本的显示时间(Presentation Time Stamp)的pts,表示样本的编码时间(Decode Time Stamp)的dts,dts主要用于视频的解码,如果解码时间与显示时间顺序一致,其值可以设置为kCMTimeInvalid。可以分别使用CMSampleBufferGetPresentationTimeStampCMSampleBufferGetDecodeTimeStamp获取pts和dts。

  • 格式信息。格式信息封装在CMFormatDescription对象中。可以分别使用CMVideoFormatDescriptionGetCodecTypeCMVideoFormatDescriptionGetDimensions获取编码类型和视频尺寸。除此之外还可以使用CMGetAttachment检索字典获取元数据:

CFDictionaryRef metadataDictionary = CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary", NULL);

3.3 CVPixelBuffer

CVPixelBufferRef是像素缓冲区类型,像素缓冲区类型基于图像缓冲区类型,属于Core Video框架的数据类型。

typedef CVImageBufferRef CVPixelBufferRef;

CVPixelBufferRef里包含很多图片相关属性,比较重要的有widthheightPixelFormatType等。除了常见的RGB32以外,还可以支持比如 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange这种YUV多平面的数据格式,通过CVPixelBufferGetBaseAddressOfPlane可以得到每个平面的数据指针。如果使用CPU访问像素数据,在获取Address之前需要调用CVPixelBufferLockBaseAddress

CMSampleBuffer的一个实例在对应的CVPixelBuffer中包含了视频帧的数据。我们可以使用CMSampleBufferGetImageBuffer获取:

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);

四、 注意事项

4.1 隐私权限

AVFoundation中的Capture模块涉及使用设备的相机和麦克风,对图片与视频文件的读取与保存涉及对用户相册的访问,这些都被苹果列为用户的隐私权限,我们需要在Xcode的info.plist添加响应的申请访问权限说明。下面列举了部分权限与其对应的key值。

隐私数据key
相册(读权限)Privacy - Photo Library Usage Description
相册(写权限)Privacy - Photo Library Additions Usage Description
麦克风Privacy - Microphone Usage Description
相机Privacy - Camera Usage Description

4.2 配置AVAudioSession

iOS设备的音频环境相对mac os较为复杂,苹果提供了AVAudioSession音频会话,在应用程序和操作系统之间扮演中间人的角色,我们只需指明应用程序的音频行为即可将对音频行为的管理委托给AVAudioSession。默认的音频会话预配置了以下行为:

  • 支持音频播放,但不允许录制音频。
  • 静音模式会使应用程序播放的任何音频静音。
  • 锁定设备会使app的音频静音。
  • 当应用程序播放音频时,它会使任何其他背景音频静音。 iOS提供六个Category可供选择:

AVAudioSessionCategoryAudioProcessing在iOS10.0已经废弃。

Category播放/录制是否中断其他音频静音或锁屏模式是否静音
SoloAmbient仅播放
Ambient仅播放
MultiRoute播放和录制
PlayAndRecord播放和录制默认YES,可以重写为NO
Playback仅播放默认YES,可以重写为NO
Record仅录制否(锁屏下仍可录制,需要配置后台模式UIBackgroundModes)

除此之外,音频会话还提供了用于监听如电话呼入、闹钟响起引起的音频中断的通知AVAudioSessionInterruptionNotification,以及例如耳机插入造成的路由线路变更的通知AVAudioSessionRouteChangeReason

4.3 多线程

AVFoundation的构建考虑到了当前的硬件环境和应用程序,其设计过程高度依赖多线程机制。我们在使用的过程中,要明确所调用的API默认工作在哪个线程,或者需要工作在哪个线程,保证对UI的处理及时回到主线程来进行,避免耗时的操作阻塞主线程。

总结

本篇作为AVFoundation系列文章的开篇,主要介绍了AVFoundation的概述和各模块的基本功能,之后通过AVAssets模块的学习我们建立了一个从轨道(track)的角度看待音视频文件的视野,了解了常用的数据类型以及使用AVFoundation的注意事项。下一篇我们将从短视频编辑第一步素材的添加与处理方面介绍AVFoundation框架提供的支持。

参考链接

AVFoundation AVAudioSession音频会话 CMSampleBuffer