IOS音视频(四十四)AVFoundation 之 Audio Queue Services
1. Audio Queue Services简介
Audio Queue Services,这是Core Audio的Audio Toolbox框架中的一个C编程接口。
- 那么什么是音频队列服务呢?
Audio Queue Services提供了一种简单、低开销的方式来在iOS和Mac OS X中录制和播放音频。
音频队列服务让您记录和播放音频的任何下列格式:
- 线性PCM。
- 您正在开发的Apple平台上原生支持的任何压缩格式。
- 用户已安装编解码器的任何其他格式。
音频队列服务是高级的。它允许您的应用程序在不了解硬件接口的情况下使用硬件记录和回放设备(如麦克风和扬声器)。它还允许您在不了解编解码器工作原理的情况下使用复杂的编解码器。
同时,音频队列服务支持一些高级功能。它提供了细粒度的定时控制,以支持预定的回放和同步。您可以使用它来同步多个音频队列的回放,并同步音频和视频。
注意:Audio Queue Services提供的特性与之前Mac OS x中的声音管理器提供的特性类似。声音管理器在Mac OS X v10.5中是不支持的,并且不能用于64位应用程序。苹果推荐所有新开发的音频队列服务,并将其作为现有Mac OS X应用程序中的声音管理器的替代品。
Audio Queue Services是一个纯C接口,可以在Cocoa应用程序中使用,也可以在Mac OS X命令行工具中使用。为了保持对音频队列服务的关注,本文中的代码示例有时通过使用Core Audio SDK中的c++类进行了简化。但是,无论是SDK还是c++语言都不需要使用音频队列服务。
1.1 音频队列
- 什么是音频队列呢?
音频队列是在iOS或Mac OS x中用于录制或播放音频的软件对象。它由AudioQueueRef不透明数据类型表示,在AudioQueue.h头文件中声明。您可以使用音频队列和其他核心音频接口,以及相对少量的自定义代码,在应用程序中创建完整的数字音频录制或回放解决方案。
一个音频队列做的工作如下:
- 连接到音频硬件
- 管理内存
- 根据需要对压缩的音频格式使用编解码器
- 调用录音和回放
1.1.1 音频队列架构
所有的音频队列具有相同的一般结构,由以下部分组成:
- 一组音频队列缓冲区,每个缓冲区都是一些音频数据的临时存储库
- 一个缓冲区队列,一个音频队列缓冲区的有序列表
- 你写的一个音频队列回调函数
根据音频队列是用于录制还是用于回放,架构会有所不同。区别在于音频队列如何连接其输入和输出,以及回调函数的角色。
1.1.2 用于录音的音频队列
使用AudioQueueNewInput函数创建的录制音频队列的结构如图1-1所示。
创建一个新的录制音频队列对象。方法如下:
func AudioQueueNewInput(_ inFormat: UnsafePointer<AudioStreamBasicDescription>,
_ inCallbackProc: AudioQueueInputCallback,
_ inUserData: UnsafeMutableRawPointer?,
_ inCallbackRunLoop: CFRunLoop?,
_ inCallbackRunLoopMode: CFString?,
_ inFlags: UInt32,
_ outAQ: UnsafeMutablePointer<AudioQueueRef?>) -> OSStatus
上面AudioQueueNewInput的参数详解如下:
inFormat
: 要记录到的压缩或未压缩音频数据格式。当记录到线性PCM时,只支持交错格式。inCallbackProc
: 一个回调函数,用于记录音频队列。当音频队列完成填充缓冲区时,音频队列调用此函数。看到AudioQueueInputCallback。inUserData
: 与回调函数一起使用的自定义数据结构。inCallbackRunLoop
: 要调用inCallbackProc参数所指向的回调函数的事件循环。如果指定NULL,则在音频队列的内部线程上调用回调。inCallbackRunLoopMode
: 调用在inCallbackProc参数中指定的回调函数的运行循环模式。通常,您传递kCFRunLoopCommonModes或使用NULL,这是等价的。您可以选择用自己的run循环创建自己的线程。有关运行循环的更多信息,请参见运行循环和CFRunLoop。inFlags
: 保留供将来使用。必须是0。outAQ
: 在输出端,新创建的录制音频队列。
还有一个音频队列输入回调方法:AudioQueueInputCallback,这个回调是当录制音频队列完成填充音频队列缓冲区时,系统调用。
在调用AudioQueueNewInput(::::::_: ::)函数时,指定一个录制音频队列回调。每次其记录音频队列用新音频数据填充音频队列缓冲区时,都会调用回调。通常,回调将数据写入文件或其他缓冲区,然后重新对音频队列缓冲区进行排队以接收更多数据。
typealias AudioQueueInputCallback = (UnsafeMutableRawPointer?, AudioQueueRef, AudioQueueBufferRef, UnsafePointer<AudioTimeStamp>, UInt32, UnsafePointer<AudioStreamPacketDescription>?) -> Void
参数详解如下:
inUserData
:您在AudioQueueNewInput(::::::_: ::)函数的inUserData参数中指定的定制数据。通常,这包括音频队列的格式和状态信息。inAQ
:调用回调的录制音频队列。inBuffer
: 一个音频队列缓冲区,由记录音频队列新填充,包含回调需要写入的新音频数据。inStartTime
: 音频队列缓冲区启动的示例时间。此参数不用于基本记录。inNumberPacketDescriptions
: 在inBuffer参数中发送回调的音频数据包的数量。当以恒定比特率(CBR)格式录制时,音频队列将此参数设置为NULL。inPacketDescs
: 对于需要包描述的压缩格式,编码器为inBuffer参数中的音频数据生成的包描述集。当以CBR格式录制时,音频队列将此参数设置为NULL。
- 录音音频队列的输入端通常连接到外部音频硬件,如麦克风。例如,在iOS中,音频来自用户内置麦克风或耳机麦克风连接的设备。在Mac OS X的默认情况下,音频来自系统的默认音频输入设备,由用户在系统首选项中设置。
- 录制音频队列的输出端使用您编写的回调函数。当录制到磁盘时,回调将从其音频队列接收到的新音频数据的缓冲区写入音频文件。然而,录制音频队列可以以其他方式使用。例如,回调可以直接向应用程序提供音频数据,而不是将其写入磁盘。
- 如想了解关于此回调的更多信息请参考:录制音频队列回调函数。
- 每个音频队列—无论是录制还是播放—都有一个或多个音频队列缓冲区。这些缓冲区按照称为缓冲区队列的特定顺序排列。在图中,音频队列缓冲区根据它们被填入的顺序进行编号——这与它们被传递给回调的顺序相同。
1.1.3 用于回放的音频队列
回放音频队列(使用AudioQueueNewOutput函数创建)的结构如图1-2所示。
在回放音频队列中,回调位于输入端。回调负责从磁盘(或其他源)获取音频数据并将其传递给音频队列。回放回调也告诉它们的音频队列在没有更多数据播放时停止。
回放音频队列的输出通常连接到外部音频硬件,如扬声器。在iOS系统中,音频会传送到用户选择的设备上,例如,接收器或耳机。在Mac OS X的默认情况下,音频将按照用户在系统首选项中设置的方式进入系统的默认音频输出设备。
接下来认识一下AudioQueueNewOutput函数,该函数用来创建一个新的播放音频队列对象。函数定义如下:
func AudioQueueNewOutput(_ inFormat: UnsafePointer<AudioStreamBasicDescription>,
_ inCallbackProc: AudioQueueOutputCallback,
_ inUserData: UnsafeMutableRawPointer?,
_ inCallbackRunLoop: CFRunLoop?,
_ inCallbackRunLoopMode: CFString?,
_ inFlags: UInt32,
_ outAQ: UnsafeMutablePointer<AudioQueueRef?>) -> OSStatus
它的参数详解如下:
inFormat
: 要记录到的压缩或未压缩音频数据格式。当记录到线性PCM时,只支持交错格式。- 一个回调函数,用于回放音频队列。音频队列在音频队列完成获取缓冲区后调用回调。参考:AudioQueueOutputCallback。
inUserData
: 与回调函数一起使用的自定义数据结构。inCallbackRunLoop
: 要调用inCallbackProc参数所指向的回调函数的事件循环。如果指定NULL,则在音频队列的内部线程上调用回调。inCallbackRunLoopMode
: 调用在inCallbackProc参数中指定的回调函数的运行循环模式。通常,您传递kCFRunLoopCommonModes或使用NULL,这是等价的。您可以选择用自己的run循环创建自己的线程。有关运行循环的更多信息,请参见运行循环和CFRunLoop。inFlags
: 保留供将来使用。必须是0。outAQ
: 在输出时,新创建的回放音频队列对象。
- 用于回放音频队列。音频队列在音频队列完成获取缓冲区后调用回调。这个回调函数当音频队列缓冲区可重用时,由系统调用。AudioQueueOutputCallback的定义如下:
typealias AudioQueueOutputCallback = (UnsafeMutableRawPointer?, AudioQueueRef, AudioQueueBufferRef) -> Void
参数详解如下:
inUserData
:您在AudioQueueNewOutput(::::::: :::)函数的inUserData参数中指定的定制数据。通常,这包括音频队列的数据格式和状态信息。inAQ
:调用回调的回放音频队列。inBuffer
: 一个音频队列缓冲区,最近可用来填充,因为回放音频队列已经获取了它的内容。
- 如果你将你的回调函数命名为MyAudioQueueOutputCallback,你会这样声明它:
此回调函数在其关联的回放音频队列从音频队列缓冲区获取数据时调用,此时该缓冲区可用于重用。新可用的缓冲区在inBuffer参数中被发送到这个回调。通常,你写这个回调到:
- 用文件或其他缓冲区中的下一组音频数据填充新可用的缓冲区。
- 重新排队播放缓冲区。要对缓冲区重新排队,可以使用AudioQueueEnqueueBuffer(::::)或AudioQueueEnqueueBufferWithParameters(:::::::::: _:)函数。
要将此回调与回放音频队列关联,请在创建音频队列时提供对回调的引用。查看AudioQueueNewOutput(::::::_: _😃函数的inCallbackProc参数。
当系统调用此回调时,您不能假定已播放了来自新可用缓冲区的音频数据。有关如何检查声音是否已完成播放的说明,请参阅AudioQueuePropertyListenerProc回调函数的讨论。
来源:CSDN
作者:极客雨露
链接:https://blog.csdn.net/kyl282889543/article/details/104130395