更新记录
0.1.0(2026-09-06)
- 插件版本更新为
0.1.0,未新增或修改运行能力。 - 补充核心特性、平台支持、安装权限、完整接口、参数、事件、结果、状态和错误码表格。
- 补充推荐参数、输出路径、文件提交、生命周期、常见问题、隐私合规和真机验收说明。
- 明确区分源码已实现、编译已通过与自定义基座真机已验证,未提前宣传规划能力。
- 修正示例页在自定义导航模式下的状态栏避让和长页面滚动。
平台兼容性
uni-app x(4.0)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| × | × | 8.0 | 0.1.0 | 13 | 0.1.0 | × | × |
专业录音与实时音频采集
xs-audio-capture-pro 是面向 uni-app x App 的本地录音 UTS 插件。它用一套接口封装 Android 与 iOS 的麦克风权限、M4A/AAC 录音、暂停继续、停止取消、状态变化和实时音量事件。
当前版本:0.1.0
当前版本已通过 Android、iOS 插件模块和示例项目编译,但尚未完成包含本插件的双端自定义基座真机验收。本文把“源码已实现”“编译已通过”和“真机已验证”分开说明,不用编译结果代替真机录音结论。
核心特性
| 能力 | 说明 |
|---|---|
| 双端统一接口 | Android 8.0 及以上、iOS 13.0 及以上使用相同的 UTS 类型、控制方法和回调结构 |
| 本地文件录音 | 生成 M4A 容器、AAC 编码的本地音频文件,不依赖网络或云服务 |
| 完整会话控制 | 支持开始、暂停、继续、停止和取消;同一时刻只允许一个录音会话 |
| 明确状态机 | 持续返回准备中、录音中、已暂停、停止中、已完成、已取消和失败状态 |
| 实时音量 | 按配置间隔返回 0~1 归一化振幅、近似分贝和有效录音时长 |
| 参数可配置 | 支持采样率、声道数、编码码率、输出路径和音量事件间隔 |
| 文件安全提交 | 录音过程写入隐藏临时文件,正常停止后才提交最终文件;取消或失败会清理临时文件 |
| 防止误覆盖 | 目标文件已经存在时返回写入错误,不会静默覆盖原文件 |
| iOS 中断感知 | iOS 可返回系统音频中断和输入路由变化事件;中断开始会暂停录音 |
| 本地与离线 | 插件本身不联网、不上传、不转写,也不内置广告或统计组件 |
平台支持
开发环境
| 项目 | 要求 |
|---|---|
| 开发框架 | uni-app x App |
| HBuilderX | 5.0 及以上 |
| uni-app x | 4.0 及以上 |
| Android | Android 8.0 / API 26 及以上 |
| iOS | iOS 13.0 及以上 |
能力差异
| 项目 | Android | iOS |
|---|---|---|
| 最低系统版本 | Android 8.0 / API 26 | iOS 13.0 |
| 原生录音器 | MediaRecorder |
AVAudioRecorder |
| 输出格式 | M4A / AAC | M4A / AAC |
| 暂停与继续 | 支持 | 支持 |
| 音量事件 | 支持 | 支持 |
| 系统中断事件 | 暂未接入 | 支持 |
| 输入路由变化事件 | 暂未接入 | 支持 |
| 后台录音 | 暂不支持 | 暂不支持 |
| 网络依赖 | 无 | 无 |
插件只面向 App 原生平台,不支持网页、小程序和鸿蒙平台。
安装与运行
将插件目录放到项目的 uni_modules/xs-audio-capture-pro 后,从插件根目录导入公开接口:
import {
getAudioCaptureCapabilities,
getAudioCapturePermission,
requestAudioCapturePermission,
startAudioCapture,
pauseAudioCapture,
resumeAudioCapture,
stopAudioCapture,
cancelAudioCapture
} from '@/uni_modules/xs-audio-capture-pro'
不要从 utssdk 或平台子目录导入内部实现,否则后续升级可能失去兼容性。
原生权限配置
插件自带以下原生声明:
| 平台 | 声明 | 用途 |
|---|---|---|
| Android | android.permission.RECORD_AUDIO |
申请麦克风录音权限 |
| Android | 麦克风硬件为非必需特性 | 不因设备无麦克风而阻止安装 |
| iOS | NSMicrophoneUsageDescription |
向用户解释麦克风用途 |
标准基座不包含本插件的原生代码和权限配置。真机调试前需要制作包含本插件的自定义基座,再在真实 Android、iPhone 设备上检查权限弹窗、录音文件和音频中断行为。
快速开始
下面示例先在用户点击后申请权限,再开始录音。业务页面应保存返回的会话编号,并通过同一个编号控制本次录音。
import {
requestAudioCapturePermission,
startAudioCapture,
pauseAudioCapture,
resumeAudioCapture,
stopAudioCapture,
cancelAudioCapture
} from '@/uni_modules/xs-audio-capture-pro'
let currentSessionId = ''
function beginRecord() {
requestAudioCapturePermission((status) => {
if (status != 'granted') {
console.log('用户未授予麦克风权限')
return
}
currentSessionId = startAudioCapture({
format: 'm4a-aac',
sampleRate: 16000,
channels: 1,
bitRate: 64000,
outputPath: null,
meterIntervalMs: 100,
allowBackground: false
}, (event) => {
if (event.type == 'state') {
console.log('状态变化', event.previousState, event.state, event.reason)
} else if (event.type == 'meter') {
console.log('录音时长', event.durationMs, '当前音量', event.amplitude)
} else if (event.type == 'warning') {
console.log('非致命提醒', event.reason)
}
}, (result) => {
currentSessionId = ''
console.log('录音文件', result.filePath)
console.log('文件大小', result.fileSize)
console.log('有效时长', result.durationMs)
}, (error) => {
currentSessionId = ''
console.error(error.code, error.message, error.nativeCode)
})
}, (error) => {
console.error(error.code, error.message)
})
}
function pauseRecord() {
if (currentSessionId.length > 0) pauseAudioCapture(currentSessionId)
}
function resumeRecord() {
if (currentSessionId.length > 0) resumeAudioCapture(currentSessionId)
}
function finishRecord() {
if (currentSessionId.length > 0) stopAudioCapture(currentSessionId)
}
function discardRecord() {
if (currentSessionId.length > 0) cancelAudioCapture(currentSessionId)
}
接口总览
| 接口 | 返回值 | 说明 |
|---|---|---|
getAudioCaptureCapabilities() |
AudioCaptureCapabilities |
同步获取当前平台真实能力和参数范围 |
getAudioCapturePermission() |
AudioCapturePermissionStatus |
同步查询麦克风权限,不弹出系统授权框 |
requestAudioCapturePermission(onSuccess, onFail) |
void |
由明确的用户操作触发麦克风权限申请 |
startAudioCapture(options, onEvent, onSuccess, onFail) |
string |
创建录音会话,成功开始时返回会话编号 |
pauseAudioCapture(sessionId) |
void |
暂停录音,有效录音时长停止累加 |
resumeAudioCapture(sessionId) |
void |
从暂停状态继续录音 |
stopAudioCapture(sessionId) |
void |
正常结束、提交文件并调用成功回调 |
cancelAudioCapture(sessionId) |
void |
取消会话、删除临时文件,不产生成功结果 |
能力查询
getAudioCaptureCapabilities()
同步返回当前运行平台的能力,不申请权限,也不启动录音。建议在页面初始化时读取它,不要根据系统名称自行猜测能力。
| 字段 | 类型 | 当前值或含义 |
|---|---|---|
platform |
string |
android 或 ios |
available |
boolean |
当前平台实现是否可用;双端当前均返回 true |
minimumSystemVersion |
string |
当前实现要求的最低系统版本 |
formats |
string[] |
当前仅包含 m4a-aac |
sampleRates |
number[] |
公开支持的采样率列表 |
channelCounts |
number[] |
当前为单声道和双声道 |
pauseResume |
boolean |
是否支持暂停与继续,当前为 true |
meter |
boolean |
是否支持音量事件,当前为 true |
pcmFrames |
boolean |
是否支持实时 PCM 帧,当前为 false |
vad |
boolean |
是否支持语音活动检测,当前为 false |
segmentation |
boolean |
是否支持自动分片,当前为 false |
backgroundRecording |
boolean |
是否支持后台录音,当前为 false |
recovery |
boolean |
是否支持异常退出后的片段恢复,当前为 false |
offline |
boolean |
是否可在不联网时工作,当前为 true |
权限接口
getAudioCapturePermission()
| 返回值 | 说明 |
|---|---|
notDetermined |
尚未向用户询问;当前主要由 iOS 准确返回 |
granted |
已获得麦克风权限 |
denied |
权限被拒绝,或 Android 当前处于未授权状态 |
Android 的同步权限接口不能稳定区分“从未申请”和“已经拒绝”,因此未授权时统一返回 denied。不要在页面加载时自动申请权限,应在用户点击录音或授权按钮后调用权限申请接口。
requestAudioCapturePermission(onSuccess, onFail)
| 参数 | 类型 | 说明 |
|---|---|---|
onSuccess |
(status) => void |
权限流程正常结束时调用;用户拒绝也会通过 status = denied 返回 |
onFail |
(error) => void |
权限流程无法启动时调用,例如 Android 取不到当前可见页面 |
iOS 当前实现不会主动调用权限申请的失败回调;授权结果统一通过成功回调返回。
开始录音
startAudioCapture(options, onEvent, onSuccess, onFail)
创建唯一录音会话。正常开始后返回非空 sessionId;参数校验、权限或启动失败时返回空字符串,并调用 onFail。
录音参数
当前所有字段都需要显式传入,没有自动补默认值。
| 参数 | 类型 | 必填 | 建议值 | 说明 |
|---|---|---|---|---|
options.format |
AudioCaptureFormat |
是 | m4a-aac |
当前唯一支持的格式 |
options.sampleRate |
number |
是 | 16000 |
支持 8000 / 16000 / 22050 / 32000 / 44100 / 48000 |
options.channels |
number |
是 | 1 |
只能为 1 或 2 |
options.bitRate |
number |
是 | 64000 |
范围 16000~320000,单位为比特每秒 |
options.outputPath |
string \| null |
是 | null |
最终文件路径;传空值时写入应用私有目录 |
options.meterIntervalMs |
number |
是 | 100 |
音量事件间隔,范围 50~1000 毫秒 |
options.allowBackground |
boolean |
是 | false |
当前必须为 false;传 true 会返回明确错误 |
公开参数通过校验只表示进入原生录音器的支持范围。不同设备是否精确接受某组采样率、声道和码率,仍需读取最终文件并在真机上核对。
事件回调
onEvent 在录音过程中可被多次调用。所有事件使用同一结构,不适用的字段返回空值。
| 字段 | 类型 | 说明 |
|---|---|---|
type |
AudioCaptureEventType |
事件类型 |
sessionId |
string |
事件所属会话编号 |
state |
AudioCaptureState |
事件发生时的当前状态 |
previousState |
AudioCaptureState \| null |
状态事件的上一个状态,其他事件通常为空 |
timestampMs |
number |
事件产生时的毫秒时间戳 |
durationMs |
number |
不含暂停时段的有效录音时长 |
amplitude |
number \| null |
音量事件中的归一化振幅,范围约为 0~1 |
decibels |
number \| null |
音量事件中的近似 dBFS,最低截断为 -160 |
reason |
string \| null |
状态原因、中断原因、路由原因或警告内容 |
| 事件类型 | Android | iOS | 触发时机 |
|---|---|---|---|
state |
支持 | 支持 | 会话状态发生变化 |
meter |
支持 | 支持 | 录音中按配置间隔返回音量;暂停时不返回 |
interruption |
暂不支持 | 支持 | 系统音频中断开始或结束 |
routeChanged |
暂不支持 | 支持 | 耳机、蓝牙等输入路由变化 |
warning |
支持 | 支持 | 无效状态控制、会话编号不匹配或非致命音量读取失败 |
iOS 收到系统中断开始时会把录音切换为暂停状态;中断结束只发送事件,不会自动继续,业务页面需要让用户确认后主动调用继续接口。
成功结果
只有 stopAudioCapture() 正常关闭编码器并提交最终文件后,onSuccess 才会调用一次。
| 字段 | 类型 | 说明 |
|---|---|---|
sessionId |
string |
已完成的会话编号 |
filePath |
string |
最终 M4A 文件的本地绝对路径 |
fileSize |
number |
文件字节数 |
durationMs |
number |
不含暂停时段的有效录音时长 |
format |
AudioCaptureFormat |
当前固定为 m4a-aac |
sampleRate |
number |
本次请求使用的采样率参数 |
channels |
number |
本次请求使用的声道参数 |
bitRate |
number |
本次请求使用的码率参数 |
stoppedReason |
string |
当前正常结束固定为 userStopped |
结果中的采样率、声道和码率是传给平台录音器的参数,不等同于独立解析文件头得到的实测值。对音频参数有严格要求时,请在真机上用音频分析工具复核最终文件。
失败回调
onFail 接收结构化错误:
| 字段 | 类型 | 说明 |
|---|---|---|
code |
AudioCaptureErrorCode |
稳定的插件错误码,适合业务分支判断 |
message |
string |
面向开发者的中文错误说明 |
nativeCode |
string \| null |
平台异常类别、系统错误码或目标路径;没有时为空 |
会话控制
| 接口 | 允许状态 | 结果 |
|---|---|---|
pauseAudioCapture(sessionId) |
recording |
进入 paused,有效时长停止累加 |
resumeAudioCapture(sessionId) |
paused |
返回 recording,有效时长继续累加 |
stopAudioCapture(sessionId) |
recording、paused |
进入 stopping,成功后进入 completed 并返回文件 |
cancelAudioCapture(sessionId) |
当前有效会话 | 进入 cancelled,删除临时文件,不调用成功回调 |
在错误状态调用暂停、继续或停止不会抛出同步异常,而是向当前会话发送 warning。传入空编号或不匹配的编号也不会误控制其他会话;如果当前仍有活动会话,会收到 sessionIdMismatch 警告。
状态流转
空闲 -> 准备中 -> 录音中 <-> 已暂停 -> 停止中 -> 已完成
| | |
+----------+-----------+-> 失败
+-----------------------> 已取消
| 状态值 | 中文含义 | 说明 |
|---|---|---|
idle |
空闲 | 原生会话创建前的初始状态 |
preparing |
准备中 | 输出路径、音频会话和编码器正在准备 |
recording |
录音中 | 正在采集并累计有效时长 |
paused |
已暂停 | 暂停采集,不累计暂停时长 |
stopping |
停止中 | 正在关闭编码器并提交最终文件 |
completed |
已完成 | 文件已成功提交,随后调用成功回调 |
cancelled |
已取消 | 临时文件已清理,不产生结果文件 |
failed |
失败 | 会话失败,随后调用失败回调 |
输出路径与文件行为
outputPath 写法 |
行为 |
|---|---|
null 或空字符串 |
Android 写入应用私有 files/audio-capture;iOS 写入应用支持目录下的 audio-capture |
| 相对文件路径 | 相对于各平台应用私有目录解析 |
| 绝对文件路径 | 直接使用;调用方必须保证路径位于应用可写范围 |
file:// 路径 |
解析为本地文件路径 |
| 目录路径 | 自动在目录内生成带时间戳和会话编号的 .m4a 文件名 |
文件名没有 .m4a 后缀 |
自动补充 .m4a 后缀 |
| 目标文件已经存在 | 返回 WRITE_FAILED,不会覆盖 |
录音过程使用与最终文件同目录的隐藏 .partial 文件。正常停止并确认文件非空后,插件才把临时文件移动为最终文件;取消或致命错误会删除临时文件。因此业务层只能在成功回调后使用、上传或播放 filePath。
推荐参数
| 场景 | 采样率 | 声道 | 码率 | 音量间隔 | 说明 |
|---|---|---|---|---|---|
| 语音记录 | 16000 |
1 |
64000 |
100 |
文件较小,适合会议记录、语音备忘 |
| 日常高质量录音 | 44100 |
1 |
128000 |
100 |
兼顾音质和体积 |
| 双声道采集尝试 | 48000 |
2 |
192000 |
100 |
需要真机确认设备输入和编码器是否实际提供双声道 |
这些是起始建议,不是所有设备的实测保证。正式上线前应覆盖目标设备、耳机和蓝牙输入进行验证。
错误码
| 错误码 | 当前触发场景 | 建议处理 |
|---|---|---|
MICROPHONE_PERMISSION_DENIED |
未获得麦克风权限就开始录音 | 引导用户点击授权;被永久拒绝时引导前往系统设置 |
INVALID_ARGUMENT |
采样率、声道、码率或音量间隔超出范围 | 修正参数后重新创建会话 |
UNSUPPORTED_FORMAT |
格式不是 m4a-aac |
改用当前支持格式 |
AUDIO_SESSION_FAILED |
原生音频会话启动、暂停或继续失败 | 记录 nativeCode,结束当前流程后提示重试 |
ENCODER_FAILED |
原生编码器启动或运行中失败 | 记录设备和原生错误,避免使用未完成文件 |
WRITE_FAILED |
目录不可写、目标已存在、文件为空或最终提交失败 | 更换可写且不存在的目标路径,并检查存储空间 |
BACKGROUND_NOT_CONFIGURED |
allowBackground = true |
当前版本改为 false;不要把前台录音当作后台录音使用 |
BUSY |
已有活动会话时再次开始 | 先停止或取消当前会话 |
UNSUPPORTED |
Android 无法取得应用上下文或当前可见页面 | 确认在 App 可见页面、由用户操作调用 |
INTERRUPTED |
已预留 | 当前版本不会主动通过失败回调返回此码 |
CANCELLED |
已预留 | 当前取消通过 cancelled 状态事件表达,不调用失败回调 |
页面与生命周期建议
- 页面上持续展示当前状态、有效时长、暂停、完成和取消入口。
- 开始按钮防止重复点击;只有拿到非空会话编号后才开放控制按钮。
- 页面离开前根据业务语义主动停止或取消,不要让会话失去控制入口。
- 音量事件只用于波形、动效和静音提示,不要把它当作校准声压数据。
- 成功回调后再播放、上传或持久化路径;取消和失败都不应保留业务记录。
- 长时间录音由业务层设置计时提醒或主动停止;当前插件没有自动限时和自动分片。
常见问题
为什么查询权限返回拒绝,但用户还没看到过弹窗?
Android 当前只能稳定判断“是否已经授权”,不能稳定区分“从未申请”和“已经拒绝”。请在用户点击按钮后调用 requestAudioCapturePermission(),不要只依赖同步查询结果决定是否允许申请。
为什么开始录音返回空字符串?
创建失败时会返回空字符串,同时调用失败回调。应优先查看 error.code、error.message 和 error.nativeCode,常见原因是权限未授权、参数不合法、已有活动会话或目标路径不可写。
为什么拿到路径后文件还不能使用?
录音中只存在隐藏临时文件。只有正常调用停止并进入成功回调后,最终文件才完成提交。不要自行拼接文件名,也不要访问 .partial 文件。
取消录音为什么没有成功或失败回调?
取消是一个正常但不产出文件的终态。插件发送 state = cancelled 事件,清理临时文件,不调用录音成功回调,也不把用户主动取消当作失败。
系统中断结束后为什么没有自动继续?
iOS 中断开始会暂停录音,中断结束只发出 interruption 事件。为避免在用户不知情时重新采集,当前需要用户确认后主动继续。
能否后台、锁屏或静默录音?
当前版本不支持后台录音,allowBackground 必须为 false。插件也不提供静默启动、隐藏通知、远程偷录或电话通话录音。
能否直接获得 PCM、波形数组或自动去掉静音?
当前不支持 PCM 帧、语音活动检测、自动分片或静音自动停止。音量事件可以驱动简单的电平动画,但不能替代原始音频帧。
插件会上传或识别录音内容吗?
不会。插件只在设备本地采集并写入文件。上传、转写、内容分析和文件保留策略都由业务项目在成功回调后自行实现。
当前限制
- 仅支持
m4a-aac,不支持 WAV 或裸 PCM 文件。 - 不向 UTS 层发送实时 PCM 帧。
- 不支持语音活动检测、自动静音停止和自动分片。
- 不支持 Android 音频焦点、音频路由变化事件。
- 不支持后台录音、异常退出片段恢复和进程重启续录。
- 未完成双端自定义基座真机验收,暂不能把所有设备组合描述为已验证。
验证建议
正式使用前至少完成以下真机测试:
- 首次授权、拒绝、永久拒绝和系统设置恢复。
- 开始、暂停、继续、停止和取消的完整状态流转。
- 默认路径、自定义路径、已存在文件和不可写路径。
- 系统播放器实际播放输出文件,并独立核对容器、编码、时长、采样率和声道。
- iOS 电话、闹钟或语音助手中断,以及有线、蓝牙输入切换。
- 5 分钟、30 分钟和连续 50 次会话的稳定性。
- 前后台切换、锁屏、磁盘不足和应用被系统终止时的边界行为。
隐私与合规
插件不会赋予业务方录音的法律许可。业务方应在录音前清晰告知相关人员录音目的、保存期限和使用范围,并遵守所在地关于同意、隐私、劳动和客户服务录音的规定。
更多文档
docs/API.md:完整类型、接口、回调、状态和错误说明。docs/AUDIO_FORMATS.md:格式、参数组合、体积估算和音量数据说明。docs/BACKGROUND_AND_PRIVACY.md:权限、前后台边界、隐私与宿主检查清单。docs/FAQ.md:接入和排错问题汇总。docs/TESTING.md:双端真机测试矩阵。docs/VERIFICATION.md:当前已经完成与尚未完成的验证证据。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 4
赞赏 0
下载 12564619
赞赏 1949
赞赏
京公网安备:11010802035340号