vx 搜索『gjzkeyframe』 关注『关键帧Keyframe』来及时获得最新的音视频技术文章。
这个公众号会路线图 式的遍历分享音视频技术:音视频基础(完成) → 音视频工具(完成) → 音视频工程示例(进行中) → 音视频工业实战(准备)。
iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染
过程,并借助音视频工具来分析和理解对应的音视频数据。
在音视频工程示例这个栏目的前面 6 篇 AVDemo 文章中,我们拆解了音频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染
流程并基于 iOS 系统 API 实现了 Demo:
- iOS AVDemo(1):音频采集
- iOS AVDemo(2):音频编码
- iOS AVDemo(3):音频封装
- iOS AVDemo(4):音频解封装
- iOS AVDemo(5):音频解码
- iOS AVDemo(6):音频渲染
你可以在关注本公众号后,在公众号发送消息『AVDemo』来获取 Demo 的全部源码。
如果你看完这些 Demo,对 iOS 平台的音视频开发多多少少会有一些认识了,在这个基础上我们来总结一下 iOS 音频处理框架,以及在前面的 Demo 中我们用到的主要 API 和数据结构有哪些。
1、iOS 音频框架
当我们想要了解 iOS 的音频处理框架时,以下是我们能比较容易找到的两张官方架构图。它们分别出自 Audio Unit Hosting Guide for iOS[1] 和 Core Audio Overview[2] 这两篇文档。
iOS Audio Frameworks
Core Audio API Layers
但这两篇文档已经比较陈旧了,是多年之前的文档,以至于和最新的 iOS 15 的框架有不少的出入。而新版本的 iOS 官方技术文档也没有给出比较清晰的音频架构图。所以在这里我们就按照 Demo 中涉及的系统 API 来挑选介绍几个相关的 Framework:
- Audio Unit Framework
- Core Media Framework
- Audio Toolbox Framework
- AVFoundation Framework
2、Audio Unit Framework
Audio Unit Framework[3]:最底层的音频处理 API,功能强大,直接驱动底层硬件,提供快速、模块化的音频处理。当你要实现低延迟的音频处理
(比如 VoIP)、对合成声音进行响应式的播放
(比如音乐游戏、合成乐器声音)、实现特定的音频能力
(比如回声消除、混音、声音均衡)、实现音频处理链支持灵活组装音频处理单元
时,你可以选择使用 Audio Unit 的 API。
需要注意的是,在最新的 iOS 系统库架构中,Audio Unit Framework 的实现都已经迁移到 Audio Toolbox Framework 中去了。
下面是 Audio Unit 框架的主要模块:
1)Audio Component Services[4]:定义了发现、开启和关闭音频单元(audio unit)以及音频编解码器的接口。
常用的数据类型:
- AudioComponent[5]:表示音频组件。一种音频组件通常由 type、subtype、manufacturer 三属性来唯一标识。
- AudioComponentDescription[6]:表示音频组件的描述。其中 type、subtype、manufacturer 三属性组合起来标识一种音频组件。
- AudioComponentInstance[7]:表示音频组件的实例。通常我们使用
查找方法(AudioComponentFindNext)
来找到符合描述的音频组件,然后再去使用创建方法(AudioComponentInstanceNew)
创建一个对应的音频组件实例。
常用的接口:
- AudioComponentFindNext(...)[8]:用于查找符合描述的音频组件。
- AudioComponentGetDescription(...)[9]:用于获取一种音频组件对应的描述。
- AudioComponentInstanceNew(...)[10]:创建一个音频组件实例。
- AudioComponentInstanceDispose(...)[11]:释放一个音频组件实例。
2)Audio Unit Component Services[12]:提供了使用音频单元(audio unit)的 C 语言接口。一个音频单元(audio unit)是用来进行音频数据处理或者音频数据生成的插件单元。要发现、开启、关闭音频单元(audio unit)则可以使用 Audio Component Services。
常用的数据类型:
- AudioUnit[13],
typedef AudioComponentInstance AudioUnit;
。AudioUnit 就是一种 AudioComponentInstance。 - AudioUnitParameter[14]:表示 AudioUnit 的参数,一个 AudioUnit 参数由 scope、element、parameterID 三属性定义。
- AudioUnitProperty[15]:表示 AudioUnit 的属性,一个 AudioUnitProperty 由 scope、element、propertyID 三属性定义。
常用的接口:
- AudioUnitInitialize(...)[16]:初始化一个 AudioUnit。如果初始化成功,说明 input/output 的格式是可支持的,并且处于可以开始渲染的状态。
- AudioUnitUninitialize(...)[17]:卸载一个 AudioUnit。一旦一个 AudioUnit 被初始化后,要想改变它的状态来响应某些环境变化,就需要先卸载。这时候会使得 AudioUnit 释放它的资源。此后,调用者可以重新配置这个 AudioUnit 来适配新的环境,比如处理与之前不同的采样率。在这之后,可以重新初始化这个 AudioUnit 来应用这些更改。
- AudioUnitRender(...)[18]:渲染输入的音频数据,数据量为 inNumberOfFrames。渲染结果的数据存储在 ioData 中。注意调用方需要提供音频时间戳,这个时间戳应该满足单调递增。
- AudioUnitGetProperty(...)[19]:获取 AudioUnit 的属性。
- AudioUnitSetProperty(...)[20]:设置 AudioUnit 的属性。
- AudioUnitGetParameter(...)[21]:获取 AudioUnit 的参数。
- AudioUnitSetParameter(...)[22]:设置 AudioUnit 的参数。
常用的回调:
- AURenderCallback[23]:在以下几种情况会被系统调用:当 AudioUnit 需要输入采样数据;在一个渲染操作前;在一个渲染操作后。
- AudioUnitPropertyListenerProc[24]:当一个指定的 AudioUnit 的 property 发生改变时,会被系统调用。
3)Output Audio Unit Services[25]:提供了 start、stop 用于 I/O 的音频单元(通常是用于输出的音频单元)的 C 语言接口。
常用的接口:
AudioOutputUnitStart(...)[26]:启动一个 I/O AudioUnit,同时会启动与之连接的 AudioUnit Processing Graph。
AudioOutputUnitStop(...)[27]:关闭一个 I/O AudioUnit,同时会关闭与之连接的 AudioUnit Processing Graph。
3、Core Media Framework
Core Media Framework[28]:定义和封装了 AVFoundation 等更上层的媒体框架需要的媒体处理流水线(包含时间信息)以及其中使用的接口和数据类型。使用 Core Media 层的接口和数据类型可以高效的处理媒体采样数据、管理采样数据队列。下面是 Core Media 框架的主要模块:
1)Sample Processing[29]:采样数据处理。常用的数据类型:
- CMSampleBuffer[30]:系统用来在音视频处理的 pipeline 中使用和传递媒体采样数据的核心数据结构。你可以认为它是 iOS 音视频处理 pipeline 中的流通货币,摄像头采集的视频数据接口、麦克风采集的音频数据接口、编码和解码数据接口、读取和存储视频接口、视频渲染接口等等,都以它作为参数。通常,CMSampleBuffer 中要么包含一个或多个媒体采样的 CMBlockBuffer,要么包含一个 CVImageBuffer。
- CMSampleBufferCreateReady(...)[31]:基于媒体数据创建一个 CMSampleBuffer。
- CMSampleBufferCreate(...)[32]:创建一个 CMSampleBuffer,支持设置数据已准备好的回调。
- CMSampleBufferSetDataBufferFromAudioBufferList(...)[33]:为指定的 CMSampleBuffer 创建其对应的 CMBlockBuffer,其中的数据拷贝自 AudioBufferList。
- CMSampleBufferGetFormatDescription(...)[34]:返回 CMSampleBuffer 中的采样数据对应的 CMFormatDescription。
- CMSampleBufferGetDataBuffer(...)[35]:返回 CMSampleBuffer 中的 CMBlockBuffer。注意调用方不会持有返回的 CMBlockBuffer,如果想要维护指向它的指针,需要显式 retain 一下。
- CMSampleBufferGetPresentationTimeStamp(...)[36]:获取 CMSampleBuffer 中所有采样的最小的 pts 时间戳。
- CMBlockBuffer[37]:一个或多个媒体采样的的裸数据。其中可以封装:音频采集后、编码后、解码后的数据(如:PCM 数据、AAC 数据);视频编码后的数据(如:H.264 数据)。
- CMBlockBufferCreateWithMemoryBlock(...)[38]:基于内存数据创建一个 CMBlockBuffer。
- CMBlockBufferGetDataPointer(...)[39]:获取访问 CMBlockBuffer 中数据的地址。
- CMFormatDescription[40]:用于描述 CMSampleBuffer 中采样的格式信息。
- CMFormatDescriptionCreate(...)[41]:创建一个 CMFormatDescription。
- CMAudioFormatDescription[42]:
typedef CMFormatDescriptionRef CMAudioFormatDescriptionRef;
。CMAudioFormatDescription 是一种 CMFormatDescriptionRef。 - CMAudioFormatDescriptionCreate(...)[43]:基于 AudioStreamBasicDescription 来创建一个 CMAudioFormatDescription。
- CMAudioFormatDescriptionGetStreamBasicDescription(...)[44]:返回一个指向 CMFormatDescription(通常应该是一个 CMAudioFormatDescription) 中的 AudioStreamBasicDescription 的指针。如果是非音频格式,就返回 NULL。
- CMAttachment[45]:为 CMSampleBuffer 添加支持的 metadata。
这里我们还要补充介绍 CoreAudioTypes Framework 中的几种数据类型:
- AudioStreamBasicDescription[46]:用于描述音频流数据格式信息,比如采样位深、声道数、采样率、每帧字节数、每包帧数、每包字节数、格式标识等。
- AudioBuffer[47]:存储并描述音频数据的缓冲区。mData 中存储着数据。可以存储两种不同类型的音频:1)单声道音频数据;2)多声道的交错音频数据,这时候 mNumberChannels 指定了声道数。
- AudioBufferList[48]:一组 AudioBuffer。
- AudioTimeStamp[49]:从多维度来表示一个时间戳的数据结构。
2)Time Representation[50]:时间信息表示。常用的数据类型:
- CMTime[51]:用 value/timescale 的方式表示时间。这样可以解决浮点运算时的精度损失问题。timescale 表示时间刻度,通常在处理视频内容时常见的时间刻度为 600,这是大部分常用视频帧率 24fps、25fps、30fps 的公倍数,音频数据常见的时间刻度就是采样率,比如 44100 或 48000。
- CMTimeRange[52]:用 start+duration 的方式表示一段时间。
- CMSampleTimingInfo[53]:一个 CMSampleBuffer 的时间戳信息,包括 pts、dts、duration。
3)Queues[54]:数据容器。常用的数据类型:
- CMSimpleQueue[55]:一个简单地、无锁的 FIFO 队列,可以放
(void *)
元素,元素不能是 NULL 或 0,如果元素是指向分配内存的指针,其内存生命周期要在外面自己管理。可以用作音视频采样数据(CMSampleBufferRef)的队列,不过要自己加锁。 - CMBufferQueue[56]:支持存储任何 CFTypeRef 类型的数据,但是数据类型需要有 duration 的概念,在创建 CMBufferQueue 的时候,会有一些回调,其中一个必须的回调是要返回队列中对象的 duration。CMBufferQueue 是设计用于在生产者/消费者模型中在不同的线程中读写数据。通常是两个线程(一个是生产者入队线程,一个是消费者出队线程),当然更多的线程也是可以的。
- CMMemoryPool[57]:内存池容器,对使用大块的内存有优化。一个 CMMemoryPool 的实例实际上维护一个最近释放内存的池子用于内存分配服务。这样的目的是加快随后的内存分配。在需要重复分配大块内存时,比如输出视频编码数据,可以使用这个数据结构。
4、Audio Toolbox Framework
Audio Toolbox Framework[58]:提供了音频录制、播放、流解析、编码格式转换、Audio Session 管理等功能接口。下面是 Audio Toolbox 框架的主要模块:
1)Audio Units[59]:音频单元。关于 Audio Unit 的内容,还可以参考上面讲到的 Audio Unit Framework。
- Audio Unit v3 Plug-Ins[60]:基于 AUv3 应用扩展接口来提供自定义的音效、乐器及其他音频能力。
- Audio Components[61]:定义了发现、加载、配置和关闭音频组件(包括音频单元(audio unit)、音频编解码器(audio codec))的接口。
- Audio Unit v2 (C) API[62]:配置一个音频单元(audio unit)以及进行音频渲染。
- Audio Unit Properties[63]:获取有关内置混音器、均衡器、滤波器、特效及音频应用扩展的信息。
- Audio Unit Voice I/O[64]:配置系统语音处理、响应语音事件。
2)Playback and Recording[65]:音频播放和录制。
- Audio Queue Services[66]:提供了简单的、低开销的方式来录制和播放音频的 C 语言接口。支持 Linear PCM、AAC 的录制和播放。实现了连接音频硬件、管理内存、根据需要使用解码器解码音频、调解录音和播放。但是要实现低延迟、回声消除、混音等功能,还得使用 AudioUnit。
- Audio Services[67]:提供了一组 C 语言接口来实现播放短声或触发 iOS 设备的振动效果。
- Music Player[68]:支持播放一组音轨,并管理播放的各种的事件。
3)Audio Files and Formats[69]:音频文件和格式。
- Audio Format Services[70]:获取音频格式和编解码器的信息。
- Audio File Services[71]:从磁盘或内存读写各种音频数据。
- Extended Audio File Services[72]:通过组合 Audio File Services 和 Audio Converter Services 来提供读写音频编码文件或 LPCM 音频文件。
- Audio File Stream Services[73]:解析音频流数据。
- Audio File Components[74]:获取音频文件格式以及文件中包含的数据的信息。
- Core Audio File Format[75]:解析 Core Audio 文件的结构。
4)Utilities[76]:其他音频功能支持。
- Audio Converter Services[77]:音频编解码。支持 LPCM 各种格式转换,以及 LPCM 与编码格式(如 AAC)的转换。常用的接口:
AudioConverterNew(...)[78]:根据指定的输入和输出音频格式创建对应的转换器(编解码器)实例。
AudioConverterNewSpecific(...)[79]:根据指定的 codec 来创建一个新的音频转换器(编解码器)实例。
AudioConverterReset(...)[80]:重置音频转换器(编解码器)实例,并清理它的缓冲区。
AudioConverterDispose(...)[81]:释放音频转换器(编解码器)实例。
AudioConverterGetProperty(...)[82]:获取音频转换器(编解码器)的属性。
AudioConverterSetProperty(...)[83]:设置音频转换器(编解码器)的属性。
AudioConverterConvertBuffer(...)[84]:只用于一种特殊的情况下将音频数据从一种 LPCM 格式转换为另外一种,并且前后采样率一致。这个接口不支持大多数压缩编码格式。
AudioConverterFillComplexBuffer(...)[85]:转换(编码)回调函数提供的音频数据,支持不交错和包格式。大部分情况下都建议用这个接口,除非是要将音频数据从一种 LPCM 格式转换为另外一种。
AudioConverterComplexInputDataProc[86]:为
AudioConverterFillComplexBuffer(...)
接口提供输入数据的回调。Audio Codec[87]:提供了支持将音频数据进行编码格式转换的 API。具体支持哪些编码格式取决于系统提供了哪些编解码器。
5、AVFoundation Framework
AVFoundation Framework[88] 是更上层的面向对象的一个音视频处理框架。它提供了音视频资源管理、相机设备管理、音视频处理、系统级音频交互管理的能力,功能非常强大。如果对其功能进行细分,可以分为如下几个模块:
- Assets,音视频资源管理。
- Playback,媒体播放及自定义播放行为支持。
- Capture,内置及外置的相机、麦克风等采集设备管理,图片、音视频录制。
- Editing,音视频编辑。
- Audio,音频播放、录制和处理,App 系统音频行为配置。
- Speech,文本语音转换。
在我们前面的 Demo 中封装 Muxer 和 Demuxer 及设置 AudioSession 时会用到 AVFoundation Framework 的一些能力,我们这里对应地介绍一下。
- AVAssetWriter[89]:支持将媒体数据写入 QuickTime 或 MPEG-4 格式的文件中,支持对多轨道的媒体数据进行交错处理来提高播放和存储的效率,支持对媒体采样进行转码,支持写入 metadata。需要注意的是,一个 AVAssetWriter 实例只能对应写一个文件,如果要写入多个文件,需要创建多个 AVAssetWriter 实例。
- canAddInput:[90]:检查 AVAssetWriter 是否支持添加对应的 AVAssetWriterInput。
- addInput:[91]:给 AVAssetWriter 添加一个 AVAssetWriterInput。注意必须在 AVAssetWriter 开始写入之前添加。
- startWriting[92]:开始写入。必须在配置好 AVAssetWriter 添加完 AVAssetWriterInput 做好准备后再调用这个方法。在调用完这个方法后,需要调用
startSessionAtSourceTime:
开始写入会话,此后就可以使用对应的 AVAssetWriterInput 来写入媒体采样数据。 - startSessionAtSourceTime:[93]:开启写入会话。在
startWriting
后调用,在写入媒体采样数据之前调用。 - endSessionAtSourceTime:[94]:结束写入会话。结束时间是会话结束时样本数据在时间轴上的时刻。如果没有显示调用这个方法,系统会在你调用
finishWritingWithCompletionHandler:
结束写入时自动调用。 - finishWritingWithCompletionHandler:[95]:标记 AVAssetWriter 的所有 input 为结束,完成写入。为了保证 AVAssetWriter 完成所有采样数据的写入,要在调用添加数据正确返回后调用这个方法。
- cancelWriting[96]:取消创建输出文件。如果 AVAssetWriter 的状态是 Failed 或 Completed,调用这个方法无效,否则,调用它会阻塞调用线程,直到会话取消完成。如果 AVAssetWriter 已经创建了输出文件,调用这个方法会删除这个文件。
- AVAssetWriterInput[97]:用于向 AVAssetWriter 实例的输出文件的一个轨道添加媒体采样数据。一个实例只能对应一个轨道媒体数据或 metadata 数据的写入,当使用多个实例向多个轨道写入数据时,需要注意检查 AVAssetWriterInput 的 readyForMoreMediaData 属性。
- expectsMediaDataInRealTime[98]:输入是否为实时数据源,比如相机采集。当设置这个值为 YES 时,会优化用于实时使用的输入来精准计算
readyForMoreMediaData
的状态。 - readyForMoreMediaData[99]:表示 AVAssetWriterInput 是否已经准备好接收媒体数据。
- requestMediaDataWhenReadyOnQueue:usingBlock:[100]:告诉 AVAssetWriterInput 在方便的时候去请求数据并写入输出文件。在对接拉取式的数据源时,可以用这个方法。
- appendSampleBuffer:[101]:通过 AVAssetWriterInput 向输出文件添加媒体数据,但是添加之前媒体数据的顺序需要自己处理。注意,调用这个方法添加采样数据后,不要更改采样数据的内容。
- markAsFinished[102]:标记 AVAssetWriterInput 为完成,表示已经完成向它添加媒体数据了。
- expectsMediaDataInRealTime[98]:输入是否为实时数据源,比如相机采集。当设置这个值为 YES 时,会优化用于实时使用的输入来精准计算
- AVAssetReader[103]:用于从 AVAsset 资源中读取媒体数据。这个 AVAsset 可以是 QuickTime 或 MPEG-4 文件,也可以是编辑创作的 AVComposition。
- canAddOutput:[104]:检查 AVAssetReader 是否支持添加对应的 AVAssetReaderOutput。
- addOutput:[105]:给 AVAssetReader 添加一个 AVAssetReaderOutput。注意必须在 AVAssetReader 开始读取之前添加。
- startReading[106]:开始读取。
- cancelReading[107]:在读完数据之前取消读取可以调用这个接口。
- AVAssetReaderOutput[108]:一个抽象类,定义了从 AVAsset 资源中读取媒体采样数据的接口。通常我们可以使用
AVAssetReaderTrackOutput
、AVAssetReaderVideoCompositionOutput
等具体的实现类。 - AVAssetReaderTrackOutput[109]:
- alwaysCopiesSampleData[110]:是否总是拷贝采样数据。如果要修改读取的采样数据,可以设置 YES,否则就设置 NO,这样性能会更好。
- copyNextSampleBuffer[111]:从 Output 拷贝下一个 CMSampleBuffer。
- AVAudioSession[112]:在最新版本的 iOS 系统库中,AVAudioSession 已经迁移到 AVFAudio Framework 中了。AVAudioSession 是系统用来管理 App 对音频硬件资源的使用的,比如:设置当前 App 与其他 App 同时使用音频时,是否混音、打断或降低其他 App 的声音;手机静音键打开时是否还可以播放声音;指定音频输入或者输出设备;是否支持录制或边录制边播放;声音被打断时的通知。我们这里只简单介绍下 Demo 中用到的接口:
- setCategory:withOptions:error:[113]:设置 AudioSession 的类型和选项参数。比如类型为 AVAudioSessionCategoryPlayback 表示支持播放;AVAudioSessionCategoryPlayAndRecord 表示同时支持播放和录制等等。
- setMode:error:[114]:设置 AudioSession 的模式。AudioSession 的类型和模式一起决定了 App 如何使用音频。通常需要在激活 AudioSession 之前设置类型和模式。比如模式为 AVAudioSessionModeVideoRecording 表示当期要录制视频;AVAudioSessionModeVoiceChat 表示语音聊天。
- setActive:withOptions:error:[115]:激活或释放 AudioSession 的使用。
以上这些框架及 API 基本上可以覆盖我们在前面的 Demo 中用到的能力了。
参考资料
[1]
Audio Unit Hosting Guide for iOS: https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html
[2]
Core Audio Overview: https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/CoreAudioOverview/CoreAudioEssentials/CoreAudioEssentials.html
[3]
Audio Unit: https://developer.apple.com/documentation/audiounit?language=objc
[4]
Audio Component Services: https://developer.apple.com/documentation/audiounit/audio_component_services?language=objc
[5]
AudioComponent: https://developer.apple.com/documentation/audiotoolbox/audiocomponent?language=objc
[6]
AudioComponentDescription: https://developer.apple.com/documentation/audiotoolbox/audiocomponentdescription?language=objc
[7]
AudioComponentInstance: https://developer.apple.com/documentation/audiotoolbox/audiocomponentinstance?language=objc
[8]
AudioComponentFindNext(...): https://developer.apple.com/documentation/audiotoolbox/1410445-audiocomponentfindnext?language=objc
[9]
AudioComponentGetDescription(...): https://developer.apple.com/documentation/audiotoolbox/1410523-audiocomponentgetdescription?language=objc
[10]
AudioComponentInstanceNew(...): https://developer.apple.com/documentation/audiotoolbox/1410465-audiocomponentinstancenew?language=objc
[11]
AudioComponentInstanceDispose(...): https://developer.apple.com/documentation/audiotoolbox/1410508-audiocomponentinstancedispose
[12]
Audio Unit Component Services: https://developer.apple.com/documentation/audiounit/audio_component_services?language=objc
[13]
AudioUnit: https://developer.apple.com/documentation/audiotoolbox/audiounit?language=objc
[14]
AudioUnitParameter: https://developer.apple.com/documentation/audiotoolbox/audiounitparameter?language=objc
[15]
AudioUnitProperty: https://developer.apple.com/documentation/audiotoolbox/audiounitproperty?language=objc
[16]
AudioUnitInitialize(...): https://developer.apple.com/documentation/audiotoolbox/1439851-audiounitinitialize?language=objc
[17]
AudioUnitUninitialize(...): https://developer.apple.com/documentation/audiotoolbox/1438415-audiounituninitialize?language=objc
[18]
AudioUnitRender(...): https://developer.apple.com/documentation/audiotoolbox/1438430-audiounitrender?language=objc
[19]
AudioUnitGetProperty(...): https://developer.apple.com/documentation/audiotoolbox/1439840-audiounitgetproperty?language=objc
[20]
AudioUnitSetProperty(...): https://developer.apple.com/documentation/audiotoolbox/1440371-audiounitsetproperty?language=objc
[21]
AudioUnitGetParameter(...): https://developer.apple.com/documentation/audiotoolbox/1440055-audiounitgetparameter?language=objc
[22]
AudioUnitSetParameter(...): https://developer.apple.com/documentation/audiotoolbox/1438454-audiounitsetparameter?language=objc
[23]
AURenderCallback: https://developer.apple.com/documentation/audiotoolbox/aurendercallback?language=objc
[24]
AudioUnitPropertyListenerProc: https://developer.apple.com/documentation/audiotoolbox/audiounitpropertylistenerproc?language=objc
[25]
Output Audio Unit Services: https://developer.apple.com/documentation/audiounit/output_audio_unit_services?language=objc
[26]
AudioOutputUnitStart(...): https://developer.apple.com/documentation/audiotoolbox/1439763-audiooutputunitstart?language=objc
[27]
AudioOutputUnitStop(...): https://developer.apple.com/documentation/audiotoolbox/1440513-audiooutputunitstop?language=objc
[28]
Core Media: https://developer.apple.com/documentation/coremedia?language=objc
[29]
Sample Processing: https://developer.apple.com/documentation/coremedia?language=objc
[30]
CMSampleBuffer: https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc
[31]
CMSampleBufferCreateReady(...): https://developer.apple.com/documentation/coremedia/1489513-cmsamplebuffercreateready?language=objc
[32]
CMSampleBufferCreate(...): https://developer.apple.com/documentation/coremedia/1489723-cmsamplebuffercreate?language=objc
[33]
CMSampleBufferSetDataBufferFromAudioBufferList(...): https://developer.apple.com/documentation/coremedia/1489725-cmsamplebuffersetdatabufferfroma?language=objc
[34]
CMSampleBufferGetFormatDescription(...): https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc
[35]
CMSampleBufferGetDataBuffer(...): https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc
[36]
CMSampleBufferGetPresentationTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc
[37]
CMBlockBuffer: https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc
[38]
CMBlockBufferCreateWithMemoryBlock(...): https://developer.apple.com/documentation/coremedia/1489501-cmblockbuffercreatewithmemoryblo?language=objc
[39]
CMBlockBufferGetDataPointer(...): https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc
[40]
CMFormatDescription: https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc
[41]
CMFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc
[42]
CMAudioFormatDescription: https://developer.apple.com/documentation/coremedia/cmaudioformatdescription?language=objc
[43]
CMAudioFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489522-cmaudioformatdescriptioncreate?language=objc
[44]
CMAudioFormatDescriptionGetStreamBasicDescription(...): https://developer.apple.com/documentation/coremedia/1489226-cmaudioformatdescriptiongetstrea?language=objc
[45]
CMAttachment: https://developer.apple.com/documentation/coremedia/cmattachment?language=objc
[46]
AudioStreamBasicDescription: https://developer.apple.com/documentation/coreaudiotypes/audiostreambasicdescription?language=objc
[47]
AudioBuffer: https://developer.apple.com/documentation/coreaudiotypes/audiobuffer?language=objc
[48]
AudioBufferList: https://developer.apple.com/documentation/coreaudiotypes/audiobufferlist?language=objc
[49]
AudioTimeStamp: https://developer.apple.com/documentation/coreaudiotypes/audiotimestamp?language=objc
[50]
Time Representation: https://developer.apple.com/documentation/coremedia?language=objc
[51]
CMTime: https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc
[52]
CMTimeRange: https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc
[53]
CMSampleTimingInfo: https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc
[54]
Queues: https://developer.apple.com/documentation/coremedia?language=objc
[55]
CMSimpleQueue: https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc
[56]
CMBufferQueue: https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc
[57]
CMMemoryPool: https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc
[58]
Audio Toolbox: https://developer.apple.com/documentation/audiotoolbox?language=objc
[59]
Audio Units: https://developer.apple.com/documentation/audiotoolbox?language=objc
[60]
Audio Unit v3 Plug-Ins: https://developer.apple.com/documentation/audiotoolbox/audio_unit_v3_plug-ins?language=objc
[61]
Audio Components: https://developer.apple.com/documentation/audiotoolbox/audio_components?language=objc
[62]
Audio Unit v2 (C) API: https://developer.apple.com/documentation/audiotoolbox/audio_unit_v2_c_api?language=objc
[63]
Audio Unit Properties: https://developer.apple.com/documentation/audiotoolbox/audio_unit_properties?language=objc
[64]
Audio Unit Voice I/O: https://developer.apple.com/documentation/audiotoolbox/audio_unit_voice_i_o?language=objc
[65]
Playback and Recording: https://developer.apple.com/documentation/audiotoolbox?language=objc
[66]
Audio Queue Services: https://developer.apple.com/documentation/audiotoolbox/audio_queue_services?language=objc
[67]
Audio Services: https://developer.apple.com/documentation/audiotoolbox/audio_services?language=objc
[68]
Music Player: https://developer.apple.com/documentation/audiotoolbox/music_player?language=objc
[69]
Audio Files and Formats: https://developer.apple.com/documentation/audiotoolbox?language=objc
[70]
Audio Format Services: https://developer.apple.com/documentation/audiotoolbox/audio_format_services?language=objc
[71]
Audio File Services: https://developer.apple.com/documentation/audiotoolbox/audio_file_services?language=objc
[72]
Extended Audio File Services: https://developer.apple.com/documentation/audiotoolbox/extended_audio_file_services?language=objc
[73]
Audio File Stream Services: https://developer.apple.com/documentation/audiotoolbox/audio_file_stream_services?language=objc
[74]
Audio File Components: https://developer.apple.com/documentation/audiotoolbox/audio_file_components?language=objc
[75]
Core Audio File Format: https://developer.apple.com/documentation/audiotoolbox/core_audio_file_format?language=objc
[76]
Utilities: https://developer.apple.com/documentation/audiotoolbox?language=objc
[77]
Audio Converter Services: https://developer.apple.com/documentation/audiotoolbox/audio_converter_services?language=objc
[78]
AudioConverterNew(...): https://developer.apple.com/documentation/audiotoolbox/1502936-audioconverternew?language=objc
[79]
AudioConverterNewSpecific(...): https://developer.apple.com/documentation/audiotoolbox/1503356-audioconverternewspecific?language=objc
[80]
AudioConverterReset(...): https://developer.apple.com/documentation/audiotoolbox/1503102-audioconverterreset?language=objc
[81]
AudioConverterDispose(...): https://developer.apple.com/documentation/audiotoolbox/1502671-audioconverterdispose?language=objc
[82]
AudioConverterGetProperty(...): https://developer.apple.com/documentation/audiotoolbox/1502731-audioconvertergetproperty?language=objc
[83]
AudioConverterSetProperty(...): https://developer.apple.com/documentation/audiotoolbox/1501675-audioconvertersetproperty?language=objc
[84]
AudioConverterConvertBuffer(...): https://developer.apple.com/documentation/audiotoolbox/1503345-audioconverterconvertbuffer?language=objc
[85]
AudioConverterFillComplexBuffer(...): https://developer.apple.com/documentation/audiotoolbox/1503098-audioconverterfillcomplexbuffer?language=objc
[86]
AudioConverterComplexInputDataProc: https://developer.apple.com/documentation/audiotoolbox/audioconvertercomplexinputdataproc?language=objc
[87]
Audio Codec: https://developer.apple.com/documentation/audiotoolbox/audio_codec?language=objc
[88]
AVFoundation Framework: https://developer.apple.com/documentation/avfoundation?language=objc
[89]
AVAssetWriter: https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc
[90]
canAddInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc
[91]
addInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc
[92]
startWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc
[93]
startSession(atSourceTime:): https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc
[94]
endSessionAtSourceTime:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc
[95]
finishWritingWithCompletionHandler:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc
[96]
cancelWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc
[97]
AVAssetWriterInput: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[98]
expectsMediaDataInRealTime: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc
[99]
readyForMoreMediaData: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc
[100]
requestMediaDataWhenReadyOnQueue:usingBlock:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[101]
appendSampleBuffer:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc
[102]
markAsFinished: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc
[103]
AVAssetReader: https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc
[104]
canAddOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc
[105]
addOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc
[106]
startReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc
[107]
cancelReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc
[108]
AVAssetReaderOutput: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc
[109]
AVAssetReaderTrackOutput: https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc
[110]
alwaysCopiesSampleData: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc
[111]
copyNextSampleBuffer: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc
[112]
AVAudioSession: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
[113]
setCategory:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc
[114]
setMode:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc
[115]
setActive:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
- 完 -
推荐阅读