vx 搜索『gjzkeyframe』 关注『关键帧Keyframe』来及时获得最新的音视频技术文章。
这个公众号会路线图 式的遍历分享音视频技术:音视频基础(完成) → 音视频工具(完成) → 音视频工程示例(进行中) → 音视频工业实战(准备)。
iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染
过程,并借助音视频工具来分析和理解对应的音视频数据。
在音视频工程示例这个栏目的 13 篇 AVDemo 文章中,我们拆解了音频和视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染
流程并基于 iOS 系统 API 实现了 Demo:
- 音频 Demo 合集:
-
- iOS AVDemo(1):音频采集
- iOS AVDemo(2):音频编码
- iOS AVDemo(3):音频封装
- iOS AVDemo(4):音频解封装
- iOS AVDemo(5):音频解码
- iOS AVDemo(6):音频渲染
- 视频 Demo 合集:
-
- iOS AVDemo(7):视频采集
- iOS AVDemo(8):视频编码
- iOS AVDemo(9):视频封装
- iOS AVDemo(10):视频解封装
- iOS AVDemo(11):音视频转封装
- iOS AVDemo(12):视频解码
- iOS AVDemo(13):视频渲染
如果你看完这些 Demo,对 iOS 平台的音视频开发多多少少会有一些认识了。在《iOS 音频处理框架及重点 API 合集》一文中,我们总结了一下 iOS 音频处理框架以及音频相关的 Demo 中用到的主要 API 和数据结构。
接下来,我们再来总结一下 iOS 视频处理框架以及视频相关的 Demo 中用到的主要 API 和数据结构。
1、iOS 视频框架
当我们想要了解 iOS 的视频处理框架时,以下是我们能比较容易找到的两张官方架构图。它们出自 AVFoundation Programming Guide[1] 这篇已经过陈旧过时的文档。
时至今日,iOS 平台的视频处理框架已经有了很多更新,上图中很多在 OS X 上的模块也有了 iOS 版本的实现。虽然有变化,但是上面的架构图,对我们了解现在 iOS 平台的视频处理框架还是有参考价值的。
根据我们的视频 Demo 中涉及的系统 API,我们这里挑选介绍几个相关的 Framework:
- Video Toolbox Framework
- Core Media Framework
- Core Video Framework
- AVFoundation Framework
2、Video Toolbox Framework
Video Toolbox Framework[2] 主要用于支持视频硬编码和硬解码。这里我们主要介绍一下编码和解码相关的 API:
1)Data Compression[3]:通过一个编码 Session 来管理对输入视频数据的压缩操作。
- VTCompressionSession[4]:编码器 Session。
-
- VTCompressionSessionCreate(...)[5]:创建编码器 Session。
- VTSessionSetProperty(...)[6]:设置编解码器 Session 的属性。
- VTCompressionSessionPrepareToEncodeFrames(...)[7]:让编码器尽量初始化编码需要的资源。这个方法调用是可选的,如果没有调用这个方法,所有编码需要的资源初始化会在第一次调用
VTCompressionSessionEncodeFrame(...)
时做。 - VTCompressionSessionEncodeFrame(...)[8]:送数据给编码器编码。被编码好的数据可能不会立即返回,所以在不要修改送给编码器的
CVPixelBuffer
。 - VTCompressionOutputCallback[9]:编码数据回调。
- VTCompressionSessionCompleteFrames(...)[10]:强制编码器完成所有或者指定时间点(completeUntilPresentationTimeStamp)及之前的所有帧。
- VTCompressionSessionInvalidate(...)[11]:终止编码。调用该方法后,记得用 CFRelease 释放编码器。
- VTEncodeInfoFlags[12]:编码时返回编码操作相关信息,可以在调用
VTCompressionSessionEncodeFrame(...)
等编码接口时传入,也会在编码数据回调中收到。有如下值: -
- kVTEncodeInfo_Asynchronous[13]:表示异步编码。
- kVTEncodeInfo_FrameDropped[14]:表示该帧被丢弃。
- VTIsHardwareDecodeSupported(...)[15]:当前系统和硬件是否支持指定编解码器类型。
2)Data Decompression[16]:通过一个解码 Session 来管理对输入视频数据的解压缩操作。
- VTDecompressionSession[17]:解码器 Session。
-
- VTDecompressionSessionCreate(...)[18]:创建解码器 Session。解码数据通过传入的回调(outputCallback)返回。
- VTSessionSetProperty(...)[19]:设置编解码器 Session 的属性。
- VTDecompressionSessionDecodeFrame(...)[20]:解码送入的数据。
- VTDecompressionOutputCallbackRecord[21]:解码数据回调。
- VTDecompressionSessionFinishDelayedFrames(...)[22]:指示解码器完成所有输入数据的解码。默认情况下,解码器不会无限期的延迟解码某一帧,除非该帧在输入给解码器时被设置了
kVTDecodeFrame_EnableTemporalProcessing
这个 VTDecodeFrameFlags。这个方法会在所有延迟的帧解码输出后返回,要等待它们,则需要调用VTDecompressionSessionWaitForAsynchronousFrames(...)
。 - VTDecompressionSessionWaitForAsynchronousFrames(...)[23]:等待所有异步或延迟的帧解码完成后再返回。调用这个方法后会自动调用
VTDecompressionSessionFinishDelayedFrames(...)
,所以使用方不用自己调。 - VTDecompressionSessionInvalidate(...)[24]:终止解码。调用该方法后,记得用 CFRelease 释放解码器。
- VTDecodeInfoFlags[25]:解码时返回解码操作相关信息,可以在调用
VTDecompressionSessionDecodeFrame(...)
等解码接口时传入,也会在解码数据回调中收到。有如下值: -
- kVTDecodeInfo_Asynchronous[26]:异步解码。
- kVTDecodeInfo_FrameDropped[27]:帧被丢弃。
- kVTDecodeInfo_ImageBufferModifiable[28]:可以修改 Buffer。
- VTDecodeFrameFlags[29]:用于指导解码器行为的指令。
-
- kVTDecodeFrame_EnableAsynchronousDecompression[30]:告诉解码器当前帧可以异步解码。但是非强制的。设置允许异步解码之后,解码器会同时解码几帧数据,带来的后果是,解码总体时间更短,但是前面几帧回调的时间可能长一些。
- kVTDecodeFrame_DoNotOutputFrame[31]:告诉解码器不对该帧回调解码输出数据,而是返回 NULL。某些情况我们不需要解码器输出帧,比如发生解码器状态错误的时候。
- kVTDecodeFrame_1xRealTimePlayback(...)[32]:告诉解码器可以使用低功耗模式解码,设置之后处理器消耗会变少,解码速度会变慢,通常我们不会设置这个参数,因为硬解码使用的是专用处理器,不消耗 CPU,所以越快越好。
- kVTDecodeFrame_EnableTemporalProcessing(...)[33]:通知解码器需要处理帧序,设置之后解码回调会变慢,因为无论是异步解码还是 pts、dts 不相等的时候都需要进行帧排序,会耗时,苹果官方文档不建议我们使用这个参数。
3、Core Media Framework
在前面介绍 iOS 音频处理框架时,我们已经介绍过 Core Media Framework[34] 了,这个 Framework 中定义和封装了 AVFoundation 等更上层的媒体框架需要的媒体处理流水线(包含时间信息)以及其中使用的接口和数据类型。使用 Core Media 层的接口和数据类型可以高效的处理媒体采样数据、管理采样数据队列。这里,我们着重介绍一下其中跟视频处理相关的部分。
1)Sample Processing[35]:采样数据处理。常用的数据类型:
- CMSampleBuffer[36]:系统用来在音视频处理的 pipeline 中使用和传递媒体采样数据的核心数据结构。你可以认为它是 iOS 音视频处理 pipeline 中的流通货币,摄像头采集的视频数据接口、麦克风采集的音频数据接口、编码和解码数据接口、读取和存储视频接口、视频渲染接口等等,都以它作为参数。通常,CMSampleBuffer 中要么包含一个或多个媒体采样的 CMBlockBuffer,要么包含一个 CVImageBuffer(也作 CVPixelBuffer)。
-
- kCMSampleAttachmentKey_NotSync[42]:Sync Sample 即 IDR 帧,可以用这个 key 对应的值来判断当前帧是否是 IDR 帧,当对应的值为 kCFBooleanFalse 表示是 IDR 帧。这个属性会被写入媒体文件或从媒体文件中读取。
- kCMSampleAttachmentKey_PartialSync[43]:当前帧是否 Partial Sync Sample,Partial Sync Sample 可以不依赖前序帧就完成解码(可认为是普通的 I 帧),两个连续的 Partial Sync Sample 随后的帧也可以不依赖这两帧的前序的帧完成解码。当设置一个帧为 Partial Sync Sample 时,需要同时设置
kCMSampleAttachmentKey_PartialSync
和kCMSampleAttachmentKey_NotSync
两个属性为 kCFBooleanTrue(可以认为是 I 帧,但是又区别于 IDR 帧)。这个属性会被写入媒体文件或从媒体文件中读取。 - kCMSampleAttachmentKey_DependsOnOthers[44]:当前帧是否依赖其他帧才能完成解码。如果对应的值为 kCFBooleanTrue,表示依赖。比如,P 或 B 帧。这个属性会被写入媒体文件或从媒体文件中读取。
- kCMSampleAttachmentKey_IsDependedOnByOthers[45]:表示当前帧是否被其他帧依赖。如果对应的值为 kCFBooleanFalse,表示不被其他帧依赖,这时候是可以丢掉该帧的。这个属性会被写入媒体文件或从媒体文件中读取。
- kCMSampleAttachmentKey_DisplayImmediately[46]:如果对应的值为 kCFBooleanTrue,表示当前帧应该马上渲染,即使还未到其 pts 时间。一般是在运行时来使用这个属性来触发一些渲染操作,比如
AVSampleBufferDisplayLayer
就可能用到。这个属性不会被写入媒体文件。 - kCMSampleAttachmentKey_DoNotDisplay[47]:表示当前帧是否只解码不渲染。一般是在运行时来使用这个属性来触发一些渲染操作,比如
AVSampleBufferDisplayLayer
就可能用到。这个属性不会被写入媒体文件。 - kCMSampleAttachmentKey_EarlierDisplayTimesAllowed[48]:表示后面的帧是否有更早的显示时间。
- kCMSampleAttachmentKey_HasRedundantCoding[49]:表示当前帧是否有冗余编码。
- CMSampleBufferGetFormatDescription(...)[37]:返回 CMSampleBuffer 中的采样数据对应的 CMFormatDescription。
- CMSampleBufferGetDataBuffer(...)[38]:返回 CMSampleBuffer 中的 CMBlockBuffer。注意调用方不会持有返回的 CMBlockBuffer,如果想要维护指向它的指针,需要显式 retain 一下。
- CMSampleBufferGetPresentationTimeStamp(...)[39]:获取 CMSampleBuffer 中所有采样的最小的 pts 时间戳。因为 CMSampleBuffer 中的采样是按照解码顺序存储的,展示顺序可能与解码顺序一致,也可能不一致。
- CMSampleBufferGetDecodeTimeStamp(...)[40]:获取 CMSampleBuffer 中所有采样的第一个采样的 dts 时间戳。在 CMSampleBuffer 中,采样是以解码顺序存储的,即使与展示顺序不一致。
- CMSampleBufferGetSampleAttachmentsArray(...)[41]:获取 CMSampleBuffer 中采用数据对应的附属数据(attachment)数组。这些附属数据可能有下面这些 key:
- CMBlockBuffer[50]:一个或多个媒体采样的的裸数据。其中可以封装:音频采集后、编码后、解码后的数据(如:PCM 数据、AAC 数据);视频编码后的数据(如:H.264 数据)。
-
- CMBlockBufferGetDataPointer(...)[51]:获取访问 CMBlockBuffer 中数据的地址。
- CMFormatDescription[52]:用于描述 CMSampleBuffer 中采样的格式信息。
-
- CMFormatDescriptionCreate(...)[53]:创建一个 CMFormatDescription。
- CMVideoCodecType[54]:
typedef FourCharCode CMVideoCodecType
,视频编码类型。 - CMMediaType[55]:
typedef FourCharCode CMMediaType
,媒体类型。
- CMVideoFormatDescription[56]:
typedef CMFormatDescriptionRef CMVideoFormatDescriptionRef;
CMVideoFormatDescription 是一种 CMFormatDescriptionRef。 -
- CMVideoFormatDescriptionCreate(...)[57]:基于 CMVideoCodecType 来创建一个 CMVideoFormatDescription。
- CMVideoFormatDescriptionGetDimensions(...)[58]:返回视频编码后的像素尺寸
CMVideoDimensions
。
2)Time Representation[59]:时间信息表示。常用的数据类型:
- CMTime[60]:用 value/timescale 的方式表示时间。这样可以解决浮点运算时的精度损失问题。timescale 表示时间刻度,通常在处理视频内容时常见的时间刻度为 600,这是大部分常用视频帧率 24fps、25fps、30fps 的公倍数,音频数据常见的时间刻度就是采样率,比如 44100 或 48000。
- CMTimeRange[61]:用 start+duration 的方式表示一段时间。
- CMSampleTimingInfo[62]:一个 CMSampleBuffer 的时间戳信息,包括 pts、dts、duration。
3)Queues[63]:数据容器。常用的数据类型:
- CMSimpleQueue[64]:一个简单地、无锁的 FIFO 队列,可以放
(void *)
元素,元素不能是 NULL 或 0,如果元素是指向分配内存的指针,其内存生命周期要在外面自己管理。可以用作音视频采样数据(CMSampleBufferRef)的队列,不过要自己加锁。 - CMBufferQueue[65]:支持存储任何 CFTypeRef 类型的数据,但是数据类型需要有 duration 的概念,在创建 CMBufferQueue 的时候,会有一些回调,其中一个必须的回调是要返回队列中对象的 duration。CMBufferQueue 是设计用于在生产者/消费者模型中在不同的线程中读写数据。通常是两个线程(一个是生产者入队线程,一个是消费者出队线程),当然更多的线程也是可以的。
- CMMemoryPool[66]:内存池容器,对使用大块的内存有优化。一个 CMMemoryPool 的实例实际上维护一个最近释放内存的池子用于内存分配服务。这样的目的是加快随后的内存分配。在需要重复分配大块内存时,比如输出视频编码数据,可以使用这个数据结构。
4、Core Video Framework
Core Video Framework[67] 主要用于支持数字视频及数字图像帧的处理,提供基于处理 Pipeline 的 API,并且同时支持 Metal 和 OpenGL。
这里我们主要介绍 CoreVideo Framework 中的几种数据类型:
- CVImageBuffer[68]:其中包含媒体流中 CMSampleBuffers 的格式描述、每个采样的宽高和时序信息、缓冲级别和采样级别的附属信息。缓冲级别的附属信息是指缓冲区整体的信息,比如播放速度、对后续缓冲数据的操作等。采样级别的附属信息是指单个采样的信息,比如视频帧的时间戳、是否关键帧等。其中可以封装:视频采集后、解码后等未经编码的数据(如:YCbCr 数据、RGBA 数据)。
- CVPixelBuffer[69]:
typedef CVImageBufferRef CVPixelBufferRef
,像素缓冲区。这是 iOS 平台进行视频编解码及图像处理相关最重要的数据结构之一。它是在CVImageBuffer
的基础上实现了内存存储。并且,CVPixelBuffer 还可以实现 CPU 和 GPU 共享内存,为图像处理提供更高的效率。 -
- CVPixelBufferLockBaseAddress(...)[70]:锁定 Pixel Buffer 的内存基地址。当使用 CPU 读取 Pixel 数据时,需要读取时锁定,读完解锁。如果在锁定时,带了
kCVPixelBufferLock_ReadOnly
的 lockFlags,解锁时也要带上。但是,如果使用 GPU 读取 Pixel 数据时,则没有必要锁定,反而会影响性能。 - CVPixelBufferUnlockBaseAddress(...)[71]:解锁 Pixel Buffer 的内存基地址。
- CVPixelBufferGetBaseAddress(...)[72]:返回 Pixel Buffer 的内存基地址,但是根据 Buffer 的类型及创建场景的不同,返回的值的含义也有区别。对于 Chunky Buffers,返回的是坐标 (0, 0) 像素的内存地址;对于 Planar Buffers,返回的是对应的 CVPlanarComponentInfo 结构体的内存地址或者 NULL(如果不存在 CVPlanarComponentInfo 结构体的话),所以,对于 Planar Buffers,最好用
CVPixelBufferGetBaseAddressOfPlane(...)
和CVPixelBufferGetBytesPerRowOfPlane(...)
来获取其中数据。获取 Pixel Buffer 的基地址时,需要先用CVPixelBufferLockBaseAddress(...)
加锁。 - CVPixelBufferGetHeight(...)[73]:返回 Buffer 的像素高度。
- CVPixelBufferGetWidth(...)[74]:返回 Buffer 的像素宽度。
- CVPixelBufferIsPlanar(...)[75]:判断 Pixel Buffer 是否是 Planar 类型。
- CVPixelBufferGetBaseAddressOfPlane(...)[76]:根据指定的 Plane Index 来获取对应的基地址。获取 Pixel Buffer 的基地址时,需要先用
CVPixelBufferLockBaseAddress(...)
加锁。怎么理解 Plane 呢?其实主要跟颜色模型有关,比如:存储 YUV420P 时,有 Y、U、V 这 3 个 Plane;存储 NV12 时,有 Y、UV 这 2 个 Plane;存储 RGBA 时,有 R、G、B、A 这 4 个 Plane;而在 Packed 存储模式中,因为所有分量的像素是交织存储的,所以只有 1 个 Plane。 - CVPixelBufferGetPlaneCount(...)[77]:返回 Pixel Buffer 中 Plane 的数量。
- CVPixelBufferGetBytesPerRowOfPlane(...)[78]:返回指定 Index 的 Plane 的每行字节数。
- CVPixelBufferGetHeightOfPlane(...)[79]:返回指定 Index 的 Plane 的高度。非 Planar 类型,返回 0。
- CVPixelBufferGetWidthOfPlane(...)[80]:返回指定 Index 的 Plane 的宽度。非 Planar 类型,返回 0。
- CVPixelBufferLockBaseAddress(...)[70]:锁定 Pixel Buffer 的内存基地址。当使用 CPU 读取 Pixel 数据时,需要读取时锁定,读完解锁。如果在锁定时,带了
5、AVFoundation Framework
AVFoundation Framework[81] 是更上层的面向对象的一个音视频处理框架。它提供了音视频资源管理、相机设备管理、音视频处理、系统级音频交互管理的能力,功能非常强大。如果对其功能进行细分,可以分为如下几个模块:
- Assets,音视频资源管理。
- Playback,媒体播放及自定义播放行为支持。
- Capture,内置及外置的相机、麦克风等采集设备管理,图片、音视频录制。
- Editing,音视频编辑。
- Audio,音频播放、录制和处理,App 系统音频行为配置。
- Speech,文本语音转换。
在我们前面的 Demo 中封装 Video Capture、Muxer、Demuxer 及设置 AudioSession 时会用到 AVFoundation Framework 的一些能力,我们这里对应地介绍一下。
1)Video Capture
关于 iOS 视频采集相关的架构,可以参考下面两张图:
- AVCaptureDevice[82]:为音频和视频采集会话提供输入的设备,并且可以提供相关硬件设备的控制能力,比如:摄像头选择、曝光、对焦、景深、缩放、闪光灯、夜景、帧率、白平衡、ISO、HDR、颜色空间、几何失真等等。这里不过多介绍,只介绍我们 Demo 中用到的一些接口:
-
- -lockForConfiguration:[83]:在配置硬件相关的属性时,需要先调用这个方法来锁定。
- -unlockForConfiguration:[84]:配置完后,解锁。
- -activeFormat[85]:属性,获取或设置当前采集设备采集的媒体格式。
- -activeVideoMinFrameDuration[86]:属性,获取或设置最低帧率。
- -activeVideoMaxFrameDuration:属性,获取或设置最高帧率。
- AVCaptureDeviceInput[87]:采集输入,从采集设备提供采集数据给采集会话。
- AVCaptureDeviceFormat[88]:用于采集设备的媒体格式或采集配置,比如视频分辨率、帧率等。
- AVCaptureDevicePosition[89]:采集设备物理位置。
- AVCaptureSession[90]:采集会话。用于管理采集活动,协调采集数据在采集设备和采集输出对象之间的流转。
-
- -sessionPreset[91]:获取或设置采集预设配置,设定采集输出的质量级别或码率。
- -addInput:[92]:为采集会话添加输入。
- -addOutput:[93]:为采集会话添加输出。
- -addConnection:[94]:为采集会话添加连接对象。
- AVCaptureSessionPreset[95]:采集预设配置,表示采集输出的质量级别或码率。比如:AVCaptureSessionPresetLow、AVCaptureSessionPresetHigh、AVCaptureSessionPreset1280x720、AVCaptureSessionPreset1920x1080 等。
- AVCaptureSessionRuntimeErrorNotification[96]:采集会话是否发生错误的通知。
- AVCaptureConnection[97]:在采集会话中连接一对采集输入和输出。可以设置采集视频镜像、防抖等。
-
- -videoMirrored[98]:经过 Connection 的视频是否镜像。
- -preferredVideoStabilizationMode[99]:设置采集图像防抖模式。
- AVCaptureVideoDataOutput[100]:采集视频的输出对象。提供访问视频帧进行图像处理的能力,可以通过
-captureOutput:didOutputSampleBuffer:fromConnection:
回调获取采集的数据。 -
- -setSampleBufferDelegate:queue:[101]:设置采集视频的回调和任务队列。
- -captureOutput:didOutputSampleBuffer:fromConnection::采集视频输出数据回调。
- -alwaysDiscardsLateVideoFrames[102]:采集视频输出时,当帧到的太晚是否丢弃。默认 YES。如果设置 NO,会给
-captureOutput:didOutputSampleBuffer:fromConnection:
更多时间处理帧,但是这时候内存占用可能会更大。
- AVCaptureVideoPreviewLayer[103]:可以用来渲染采集视频数据的 Core Animation Layer。
-
- -setVideoGravity:[104]:设置渲染的内容填充模式。
2)Muxer
- AVAssetWriter[105]:支持将媒体数据写入 QuickTime 或 MPEG-4 格式的文件中,支持对多轨道的媒体数据进行交错处理来提高播放和存储的效率,支持对媒体采样进行转码,支持写入 metadata。需要注意的是,一个 AVAssetWriter 实例只能对应写一个文件,如果要写入多个文件,需要创建多个 AVAssetWriter 实例。
-
- canAddInput:[106]:检查 AVAssetWriter 是否支持添加对应的 AVAssetWriterInput。
- addInput:[107]:给 AVAssetWriter 添加一个 AVAssetWriterInput。注意必须在 AVAssetWriter 开始写入之前添加。
- startWriting[108]:开始写入。必须在配置好 AVAssetWriter 添加完 AVAssetWriterInput 做好准备后再调用这个方法。在调用完这个方法后,需要调用
startSessionAtSourceTime:
开始写入会话,此后就可以使用对应的 AVAssetWriterInput 来写入媒体采样数据。 - startSessionAtSourceTime:[109]:开启写入会话。在
startWriting
后调用,在写入媒体采样数据之前调用。 - endSessionAtSourceTime:[110]:结束写入会话。结束时间是会话结束时样本数据在时间轴上的时刻。如果没有显示调用这个方法,系统会在你调用
finishWritingWithCompletionHandler:
结束写入时自动调用。 - finishWritingWithCompletionHandler:[111]:标记 AVAssetWriter 的所有 input 为结束,完成写入。为了保证 AVAssetWriter 完成所有采样数据的写入,要在调用添加数据正确返回后调用这个方法。
- cancelWriting[112]:取消创建输出文件。如果 AVAssetWriter 的状态是 Failed 或 Completed,调用这个方法无效,否则,调用它会阻塞调用线程,直到会话取消完成。如果 AVAssetWriter 已经创建了输出文件,调用这个方法会删除这个文件。
- AVAssetWriterInput[113]:用于向 AVAssetWriter 实例的输出文件的一个轨道添加媒体采样数据。一个实例只能对应一个轨道媒体数据或 metadata 数据的写入,当使用多个实例向多个轨道写入数据时,需要注意检查 AVAssetWriterInput 的 readyForMoreMediaData 属性。
-
- expectsMediaDataInRealTime[114]:输入是否为实时数据源,比如相机采集。当设置这个值为 YES 时,会优化用于实时使用的输入来精准计算
readyForMoreMediaData
的状态。 - readyForMoreMediaData[115]:表示 AVAssetWriterInput 是否已经准备好接收媒体数据。
- requestMediaDataWhenReadyOnQueue:usingBlock:[116]:告诉 AVAssetWriterInput 在方便的时候去请求数据并写入输出文件。在对接拉取式的数据源时,可以用这个方法。
- appendSampleBuffer:[117]:通过 AVAssetWriterInput 向输出文件添加媒体数据,但是添加之前媒体数据的顺序需要自己处理。注意,调用这个方法添加采样数据后,不要更改采样数据的内容。
- markAsFinished[118]:标记 AVAssetWriterInput 为完成,表示已经完成向它添加媒体数据了。
- expectsMediaDataInRealTime[114]:输入是否为实时数据源,比如相机采集。当设置这个值为 YES 时,会优化用于实时使用的输入来精准计算
3)Demuxer
- AVAssetReader[119]:用于从 AVAsset 资源中读取媒体数据。这个 AVAsset 可以是 QuickTime 或 MPEG-4 文件,也可以是编辑创作的 AVComposition。
-
- canAddOutput:[120]:检查 AVAssetReader 是否支持添加对应的 AVAssetReaderOutput。
- addOutput:[121]:给 AVAssetReader 添加一个 AVAssetReaderOutput。注意必须在 AVAssetReader 开始读取之前添加。
- startReading[122]:开始读取。
- cancelReading[123]:在读完数据之前取消读取可以调用这个接口。
- AVAssetReaderOutput[124]:一个抽象类,定义了从 AVAsset 资源中读取媒体采样数据的接口。通常我们可以使用
AVAssetReaderTrackOutput
、AVAssetReaderVideoCompositionOutput
等具体的实现类。 - AVAssetReaderTrackOutput[125]:
-
- alwaysCopiesSampleData[126]:是否总是拷贝采样数据。如果要修改读取的采样数据,可以设置 YES,否则就设置 NO,这样性能会更好。
- copyNextSampleBuffer[127]:从 Output 拷贝下一个 CMSampleBuffer。
- AVAudioSession[128]:在最新版本的 iOS 系统库中,AVAudioSession 已经迁移到 AVFAudio Framework 中了。AVAudioSession 是系统用来管理 App 对音频硬件资源的使用的,比如:设置当前 App 与其他 App 同时使用音频时,是否混音、打断或降低其他 App 的声音;手机静音键打开时是否还可以播放声音;指定音频输入或者输出设备;是否支持录制或边录制边播放;声音被打断时的通知。我们这里只简单介绍下 Demo 中用到的接口:
-
- setCategory:withOptions:error:[129]:设置 AudioSession 的类型和选项参数。比如类型为 AVAudioSessionCategoryPlayback 表示支持播放;AVAudioSessionCategoryPlayAndRecord 表示同时支持播放和录制等等。
- setMode:error:[130]:设置 AudioSession 的模式。AudioSession 的类型和模式一起决定了 App 如何使用音频。通常需要在激活 AudioSession 之前设置类型和模式。比如模式为 AVAudioSessionModeVideoRecording 表示当期要录制视频;AVAudioSessionModeVoiceChat 表示语音聊天。
- setActive:withOptions:error:[131]:激活或释放 AudioSession 的使用。
以上这些框架及 API 基本上可以覆盖我们在前面的 Demo 中用到的能力了。
参考资料
[1]
AVFoundation Programming Guide: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html
[2]
Video Toolbox Framework: https://developer.apple.com/documentation/videotoolbox?language=objc
[3]
Data Compression: https://developer.apple.com/documentation/videotoolbox?language=objc
[4]
VTCompressionSession: https://developer.apple.com/documentation/videotoolbox/vtcompressionsession?language=objc
[5]
VTCompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1428285-vtcompressionsessioncreate?language=objc
[6]
VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc
[7]
VTCompressionSessionPrepareToEncodeFrames(...): https://developer.apple.com/documentation/videotoolbox/1428283-vtcompressionsessionpreparetoenc?language=objc
[8]
VTCompressionSessionEncodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1428287-vtcompressionsessionencodeframe?language=objc
[9]
VTCompressionOutputCallback: https://developer.apple.com/documentation/videotoolbox/vtcompressionoutputcallback?language=objc
[10]
VTCompressionSessionCompleteFrames(...): https://developer.apple.com/documentation/videotoolbox/1428303-vtcompressionsessioncompletefram?language=objc
[11]
VTCompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1428295-vtcompressionsessioninvalidate?language=objc
[12]
VTEncodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags?language=objc
[13]
kVTEncodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_asynchronous?language=objc
[14]
kVTEncodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_framedropped?language=objc
[15]
VTIsHardwareDecodeSupported(...): https://developer.apple.com/documentation/videotoolbox/2887343-vtishardwaredecodesupported?language=objc
[16]
Data Decompression: https://developer.apple.com/documentation/videotoolbox?language=objc
[17]
VTDecompressionSession: https://developer.apple.com/documentation/videotoolbox/vtdecompressionsession?language=objc
[18]
VTDecompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1536134-vtdecompressionsessioncreate?language=objc
[19]
VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc
[20]
VTDecompressionSessionDecodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1536071-vtdecompressionsessiondecodefram?language=objc
[21]
VTDecompressionOutputCallbackRecord: https://developer.apple.com/documentation/videotoolbox/vtdecompressionoutputcallbackrecord?language=objc
[22]
VTDecompressionSessionFinishDelayedFrames(...): https://developer.apple.com/documentation/videotoolbox/1536101-vtdecompressionsessionfinishdela?language=objc
[23]
VTDecompressionSessionWaitForAsynchronousFrames(...): https://developer.apple.com/documentation/videotoolbox/1536066-vtdecompressionsessionwaitforasy?language=objc
[24]
VTDecompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1536093-vtdecompressionsessioninvalidate?language=objc
[25]
VTDecodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags?language=objc
[26]
kVTDecodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_asynchronous?language=objc
[27]
kVTDecodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_framedropped?language=objc
[28]
kVTDecodeInfo_ImageBufferModifiable: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_imagebuffermodifiable?language=objc
[29]
VTDecodeFrameFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags?language=objc
[30]
kVTDecodeFrame_EnableAsynchronousDecompression: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enableasynchronousdecompression?language=objc
[31]
kVTDecodeFrame_DoNotOutputFrame: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_donotoutputframe?language=objc
[32]
kVTDecodeFrame_1xRealTimePlayback(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_1xrealtimeplayback?language=objc
[33]
kVTDecodeFrame_EnableTemporalProcessing(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enabletemporalprocessing?language=objc
[34]
Core Media: https://developer.apple.com/documentation/coremedia?language=objc
[35]
Sample Processing: https://developer.apple.com/documentation/coremedia?language=objc
[36]
CMSampleBuffer: https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc
[37]
CMSampleBufferGetFormatDescription(...): https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc
[38]
CMSampleBufferGetDataBuffer(...): https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc
[39]
CMSampleBufferGetPresentationTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc
[40]
CMSampleBufferGetDecodeTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489404-cmsamplebuffergetdecodetimestamp?language=objc
[41]
CMSampleBufferGetSampleAttachmentsArray(...): https://developer.apple.com/documentation/coremedia/1489189-cmsamplebuffergetsampleattachmen?language=objc
[42]
kCMSampleAttachmentKey_NotSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_notsync?language=objc
[43]
kCMSampleAttachmentKey_PartialSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_partialsync?language=objc
[44]
kCMSampleAttachmentKey_DependsOnOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_dependsonothers?language=objc
[45]
kCMSampleAttachmentKey_IsDependedOnByOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_isdependedonbyothers?language=objc
[46]
kCMSampleAttachmentKey_DisplayImmediately: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_displayimmediately?language=objc
[47]
kCMSampleAttachmentKey_DoNotDisplay: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_donotdisplay?language=objc
[48]
kCMSampleAttachmentKey_EarlierDisplayTimesAllowed: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_earlierdisplaytimesallowed?language=objc
[49]
kCMSampleAttachmentKey_HasRedundantCoding: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_hasredundantcoding?language=objc
[50]
CMBlockBuffer: https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc
[51]
CMBlockBufferGetDataPointer(...): https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc
[52]
CMFormatDescription: https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc
[53]
CMFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc
[54]
CMVideoCodecType: https://developer.apple.com/documentation/coremedia/cmvideocodectype?language=objc
[55]
CMMediaType: https://developer.apple.com/documentation/coremedia/1564193-cmmediatype/
[56]
CMVideoFormatDescription: https://developer.apple.com/documentation/coremedia/cmvideoformatdescription?language=objc
[57]
CMVideoFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489743-cmvideoformatdescriptioncreate?language=objc
[58]
CMVideoFormatDescriptionGetDimensions(...): https://developer.apple.com/documentation/coremedia/1489287-cmvideoformatdescriptiongetdimen?language=objc
[59]
Time Representation: https://developer.apple.com/documentation/coremedia?language=objc
[60]
CMTime: https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc
[61]
CMTimeRange: https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc
[62]
CMSampleTimingInfo: https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc
[63]
Queues: https://developer.apple.com/documentation/coremedia?language=objc
[64]
CMSimpleQueue: https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc
[65]
CMBufferQueue: https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc
[66]
CMMemoryPool: https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc
[67]
Core Video Framework: https://developer.apple.com/documentation/corevideo?language=objc
[68]
CVImageBuffer: https://developer.apple.com/documentation/corevideo/cvimagebuffer-q40
[69]
CVPixelBuffer: https://developer.apple.com/documentation/corevideo/cvpixelbuffer?language=objc
[70]
CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457128-cvpixelbufferlockbaseaddress?language=objc
[71]
CVPixelBufferUnlockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1456843-cvpixelbufferunlockbaseaddress?language=objc
[72]
CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457115-cvpixelbuffergetbaseaddress?language=objc
[73]
CVPixelBufferGetHeight(...): https://developer.apple.com/documentation/corevideo/1456666-cvpixelbuffergetheight?language=objc
[74]
CVPixelBufferGetWidth(...): https://developer.apple.com/documentation/corevideo/1457241-cvpixelbuffergetwidth?language=objc
[75]
CVPixelBufferIsPlanar(...): https://developer.apple.com/documentation/corevideo/1456805-cvpixelbufferisplanar?language=objc
[76]
CVPixelBufferGetBaseAddressOfPlane(...): https://developer.apple.com/documentation/corevideo/1456821-cvpixelbuffergetbaseaddressofpla?language=objc
[77]
CVPixelBufferGetPlaneCount(...): https://developer.apple.com/documentation/corevideo/1456976-cvpixelbuffergetplanecount?language=objc
[78]
CVPixelBufferGetBytesPerRowOfPlane(...): https://developer.apple.com/documentation/corevideo/1456711-cvpixelbuffergetbytesperrowofpla?language=objc
[79]
CVPixelBufferGetHeightOfPlane(...): https://developer.apple.com/documentation/corevideo/1456698-cvpixelbuffergetheightofplane?language=objc
[80]
CVPixelBufferGetWidthOfPlane(...): https://developer.apple.com/documentation/corevideo/1456830-cvpixelbuffergetwidthofplane?language=objc
[81]
AVFoundation Framework: https://developer.apple.com/documentation/avfoundation?language=objc
[82]
AVCaptureDevice: https://developer.apple.com/documentation/avfoundation/avcapturedevice?language=objc
[83]
-lockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387810-lockforconfiguration?language=objc
[84]
-unlockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387917-unlockforconfiguration?language=objc
[85]
-activeFormat: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389221-activeformat?language=objc
[86]
-activeVideoMinFrameDuration: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389290-activevideominframeduration?language=objc
[87]
AVCaptureDeviceInput: https://developer.apple.com/documentation/avfoundation/avcapturedeviceinput?language=objc
[88]
AVCaptureDeviceFormat: https://developer.apple.com/documentation/avfoundation/avcapturedeviceformat?language=objc
[89]
AVCaptureDevicePosition: https://developer.apple.com/documentation/avfoundation/avcapturedeviceposition?language=objc
[90]
AVCaptureSession: https://developer.apple.com/documentation/avfoundation/avcapturesession?language=objc
[91]
-sessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389696-sessionpreset?language=objc
[92]
-addInput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387239-addinput?language=objc
[93]
-addOutput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387325-addoutput?language=objc
[94]
-addConnection:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389687-addconnection?language=objc
[95]
AVCaptureSessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesessionpreset?language=objc
[96]
AVCaptureSessionRuntimeErrorNotification: https://developer.apple.com/documentation/avfoundation/avcapturesessionruntimeerrornotification?language=objc
[97]
AVCaptureConnection: https://developer.apple.com/documentation/avfoundation/avcaptureconnection?language=objc
[98]
-videoMirrored: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1389172-videomirrored?language=objc
[99]
preferredVideoStabilizationMode: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1620484-preferredvideostabilizationmode?language=objc
[100]
AVCaptureVideoDataOutput: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput?language=objc
[101]
-setSampleBufferDelegate:queue:: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1389008-setsamplebufferdelegate?language=objc
[102]
-alwaysDiscardsLateVideoFrames: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1385780-alwaysdiscardslatevideoframes?language=objc
[103]
AVCaptureVideoPreviewLayer: https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer?language=objc
[104]
-setVideoGravity:: https://developer.apple.com/documentation/watchkit/wkinterfacemovie/1628130-setvideogravity?language=objc
[105]
AVAssetWriter: https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc
[106]
canAddInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc
[107]
addInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc
[108]
startWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc
[109]
startSession(atSourceTime:): https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc
[110]
endSessionAtSourceTime:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc
[111]
finishWritingWithCompletionHandler:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc
[112]
cancelWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc
[113]
AVAssetWriterInput: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[114]
expectsMediaDataInRealTime: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc
[115]
readyForMoreMediaData: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc
[116]
requestMediaDataWhenReadyOnQueue:usingBlock:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[117]
appendSampleBuffer:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc
[118]
markAsFinished: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc
[119]
AVAssetReader: https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc
[120]
canAddOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc
[121]
addOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc
[122]
startReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc
[123]
cancelReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc
[124]
AVAssetReaderOutput: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc
[125]
AVAssetReaderTrackOutput: https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc
[126]
alwaysCopiesSampleData: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc
[127]
copyNextSampleBuffer: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc
[128]
AVAudioSession: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
[129]
setCategory:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc
[130]
setMode:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc
[131]
setActive:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc