aoq-electron-sdk
v1.3.0-58586345
Published
AoqClientSdk for Electron (NAPI addon + TypeScript API)
Readme
aoq-electron-sdk
AoqClientSdk 的 Electron / Node.js SDK,提供实时音视频采集、编解码、传输与数据消息能力。
安装
npm install aoq-electron-sdk安装后 postinstall 会自动解压当前平台的原生库(macOS 的 AoqClientSdk.framework),无需额外操作。
包内同时包含各平台的原生产物,运行时按 process.platform-process.arch 自动选择:
node_modules/aoq-electron-sdk/prebuilds/
darwin-arm64/ aoq_node_ext.node + AoqClientSdk.framework + PluginOpus.framework
darwin-x64/ 同上
win32-x64/ aoq_node_ext.node + AoqClientSdk.dll + PluginOpus.dll + alivcffmpeg.dll环境要求
| 项 | 要求 | |---|---| | 操作系统 | macOS(Intel x64 / Apple Silicon arm64)、Windows x64 | | Node.js | >= 16 | | Electron | 任意版本(基于 Node-API,ABI 稳定,无需按 Electron 版本重编) |
Electron 集成配置(必读)
SDK 含原生 addon,需要两处配置,否则运行时无法加载:
1. 允许 renderer 使用 Node 模块,并把 SDK 声明为 external
vue.config.js(vue-cli-plugin-electron-builder)示例:
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
// 原生 addon 不能被 webpack 打包,须走运行时 require
externals: ['aoq-electron-sdk'],
builderOptions: {
// 打包时把 SDK 解包到 app.asar.unpacked,
// 否则 .node / .framework 无法从 asar 内加载
asarUnpack: ['node_modules/aoq-electron-sdk/**']
}
}
}
}裸 webpack 配置等价写法:
externals: { 'aoq-electron-sdk': 'commonjs2 aoq-electron-sdk' }2. 首次采集会触发系统麦克风/摄像头授权弹窗,请在应用 Info.plist 中声明
NSMicrophoneUsageDescription / NSCameraUsageDescription。
快速开始
import createAoqClientEngine, { AoqTrackType } from 'aoq-electron-sdk'
const engine = createAoqClientEngine()
// 1. 监听事件
engine.on('onConnectionStatusChange', (status) => console.log('status:', status))
engine.on('onDataMsg', (data) => console.log('recv:', Buffer.from(data).toString()))
engine.on('onError', (code, message) => console.error('error:', code, message))
// 2. 创建引擎
engine.createEngine({
workDir: '/tmp/aoq', // 日志与临时文件目录
enableDumpAudio: false,
extras: ''
})
// 3. 编解码配置(可选,不设则用默认值)
engine.setAudioEncoderConfig({
trackType: AoqTrackType.AoqTrackTypeAudio,
codecType: 2, // Opus
sampleRate: 16000, channel: 1, bitrate: 24000
})
// 4. 连接(token / sid / relayEndpoints 由业务服务端下发)
engine.connect({
token, sid, certFingerprint,
relayEndpoints: [{ routeIndex: 0, endpoint, port }],
publishTracks: [{ trackType: AoqTrackType.AoqTrackTypeAudio }],
subscribeTracks: [{ trackType: AoqTrackType.AoqTrackTypeAudio }]
})
// 5. 开启音频采集与播放
engine.startAudioCapture({ channel: 1 })
engine.startAudioPlayer({ channel: 1 })
// 6. 收发数据消息
engine.sendDataMsg('hello')
// 7. 收尾
engine.disconnect()
engine.destroy()所有接口返回 number:0 表示成功,非 0 为错误码。
API 概览
生命周期与连接
| 接口 | 说明 |
|---|---|
| createEngine(config) | 创建引擎(单例,重复调用返回同一实例) |
| destroy() | 销毁引擎;销毁后接口短路返回 -1 |
| getVersion() | 返回 SDK 版本号字符串 |
| connect(config) | 连接;状态变化由 onConnectionStatusChange 通知 |
| disconnect() | 断开连接 |
| enableSendMediaStream(trackType, enable) | 控制某路流是否发送 |
音频
| 分类 | 接口 |
|---|---|
| 采集 | startAudioCapture / stopAudioCapture / muteAudioCapture |
| 播放 | startAudioPlayer / stopAudioPlayer / pauseAudioPlayer / resumeAudioPlayer / interruptAudioPlayer |
| 编解码 | setAudioEncoderConfig / setAudioDecoderConfig |
| 文件混音 | startAudioFile / stopAudioFile / pauseAudioFile / resumeAudioFile / getAudioFileDuration / getAudioFileCurrentPosition / setAudioFilePositionMillis / setAudioFileVolume / getAudioFileVolume |
| 外部音频流 | addAudioExternalStream / pushAudioExternalStreamData / setAudioExternalStreamVolume / getAudioExternalStreamVolume / clearAudioExternalStreamBuffer / removeAudioExternalStream |
视频
| 分类 | 接口 |
|---|---|
| 采集 | startVideoCapture / stopVideoCapture |
| 编解码 | setVideoEncoderConfig / setVideoDecoderConfig |
| 外部推帧 | pushExternalVideoCapturedFrame(BGRA / I420)、pushExternalVideoEncodedFrame(JPEG) |
数据消息
sendDataMsg(data) —— data 支持 string 或 Buffer;对端消息通过 onDataMsg 事件回调。
帧观察者
engine.setAudioFrameObserver(true)
engine.enableAudioFrameObserver({ enabled: true, audioSource: 0, sampleRate: 48000, channels: 1 })
engine.on('onCapturedAudioFrame', (frame) => { /* frame.buffer 为 PCM 数据 */ })视频侧对应 setVideoFrameObserver / enableVideoFrameObserver +
onCapturedVideoFrame / onPreEncodeVideoFrame / onRemoteVideoFrame。
帧观察者为只读模式,回调中不支持回写帧数据。
事件列表
engine.on(event, handler) 支持:
onError / onWarning / onConnectionStatusChange / onStats
onAudioDeviceStateChanged / onAudioDeviceRouteChanged / onAudioFileState
onVideoDeviceStateChanged / onDataMsg
onCapturedAudioFrame / onProcessCapturedAudioFrame / onPublishAudioFrame / onPlaybackAudioFrame
onCapturedVideoFrame / onPreEncodeVideoFrame / onRemoteVideoFrame完整的参数与枚举定义见包内 types/ 目录下的 TypeScript 类型声明(IDE 可自动提示)。
视频预览与渲染
Electron renderer 是 Chromium 环境,无法嵌入原生视图,因此 SDK 不提供
setLocalView / setRemoteView。视频预览通过「帧观察者 + Canvas」实现,SDK 已内置渲染器:
import { YUVCanvasRenderer } from 'aoq-electron-sdk'
const renderer = new YUVCanvasRenderer()
renderer.bind(document.getElementById('preview')) // 传入 <canvas> 元素
engine.setVideoFrameObserver(true)
engine.enableVideoFrameObserver({
enabled: true,
videoSource: 0,
trackType: 1, // Video;Screen 为 3
format: 1 // I420
})
engine.on('onCapturedVideoFrame', (frame) => {
// frame.trackType 可区分 Video / Screen
renderer.drawFrame(frame)
})渲染填充模式(拉伸/裁剪等)用 CSS object-fit 控制 canvas 即可。
Windows 摄像头采集与消息泵
Windows 的摄像头采集依赖一个 message-only window 收开/关摄像头的消息,派发靠
加载 AoqClientSdk.dll 那个线程(即 require('aoq-electron-sdk') 所在的 JS 线程)
的 Win32 消息循环。Electron renderer 进程不派发这类窗口消息,因此 SDK 在
createEngine() 时会在该线程的 libuv 循环上挂一个 16ms 的消息泵,destroy() 时停掉。
由此带来两点约定:
- 不要在
createEngine()之后长时间阻塞 JS 线程(同步大计算、execSync等), 否则消息泵停摆,摄像头会卡在「启动中」 - 别的线程/进程里裸调 native 层(绕过本 SDK)不会有这个消息泵,摄像头不会出帧
使用建议
- 事件回调中避免重计算:native 回调经异步线程投递,高频帧事件中做耗时操作会造成堆积
- 生产应用建议走 main 进程 + IPC:本文档示例为 renderer 直接加载(便于快速接入), 生产环境把 SDK 放在 main 进程、通过 IPC 与界面通信更稳妥
destroy()后需重新createEngine()才能继续使用
常见问题
Q: 运行时报 Cannot find module '.../aoq_node_ext.node'
检查是否配置了 nodeIntegration: true 与 externals(见上文集成配置)。
Q: 打包后的应用启动即崩溃 / 找不到 framework
检查 asarUnpack 是否包含 node_modules/aoq-electron-sdk/**。原生库不能放在 asar 内。
Q: npm install 报平台不支持 / 运行时报“找不到当前平台的原生模块”
报错会列出它查找过的目录。对照 prebuilds/ 下是否有你平台对应的目录;
如果没有,说明这个版本的包未包含该平台产物(当前发布矩阵:macOS x64/arm64、Windows x64)。
Q: 采集无声音
确认已授予麦克风权限,且 Info.plist 声明了 NSMicrophoneUsageDescription。
