aoq-electron-sdk 1.2.0-57013983
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +369 -0
- package/build/Release/AoqClientSdk.framework.zip +0 -0
- package/build/Release/aoq_node_ext.node +0 -0
- package/js/AoqSdk.js +32 -0
- package/js/IAoqClientEngine.js +2 -0
- package/js/Renderer/YUVCanvasRenderer.js +67 -0
- package/js/Types.js +138 -0
- package/js/internal/AoqClientEngineInternal.js +239 -0
- package/package.json +62 -0
- package/scripts/coverage-report.js +339 -0
- package/scripts/postinstall.js +27 -0
- package/scripts/prepublish-check.js +26 -0
- package/ts/AoqSdk.ts +28 -0
- package/ts/IAoqClientEngine.ts +160 -0
- package/ts/Renderer/YUVCanvasRenderer.ts +75 -0
- package/ts/Types.ts +453 -0
- package/ts/internal/AoqClientEngineInternal.ts +347 -0
- package/types/AoqSdk.d.ts +9 -0
- package/types/IAoqClientEngine.d.ts +93 -0
- package/types/Renderer/YUVCanvasRenderer.d.ts +12 -0
- package/types/Types.d.ts +394 -0
- package/types/internal/AoqClientEngineInternal.d.ts +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# AoqClientSdk for Electron
|
|
2
|
+
|
|
3
|
+
AoqClientSdk 的 Electron 平台封装(架构参考 Agora Electron-SDK):
|
|
4
|
+
C++ NAPI addon 桥接原生 SDK,TypeScript 提供强类型 API 与事件,
|
|
5
|
+
npm 包形态供 Electron 应用消费。
|
|
6
|
+
|
|
7
|
+
## 架构
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
JS/TS 调用方 (Electron renderer / node)
|
|
11
|
+
│ import createAoqClientEngine from 'aoq-electron-sdk'
|
|
12
|
+
▼
|
|
13
|
+
ts/ TypeScript API 层
|
|
14
|
+
├── AoqSdk.ts 入口:createAoqClientEngine() 单例工厂
|
|
15
|
+
├── Types.ts 枚举/配置接口(与 AoqClientEngine.h 逐字段对齐,
|
|
16
|
+
│ 同时是 TS <-> native JSON 协议的唯一事实来源)
|
|
17
|
+
├── IAoqClientEngine.ts 引擎接口 + IAoqEngineEvents 事件定义
|
|
18
|
+
├── internal/ 内部实现:require .node、JSON 序列化、事件分发
|
|
19
|
+
└── Renderer/ YUVCanvasRenderer:I420 帧 → canvas 软件渲染
|
|
20
|
+
│ JSON 参数 / 单通道事件(call_back_with_event)+ Buffer
|
|
21
|
+
▼
|
|
22
|
+
source_code/aoq_node_ext/ C++ NAPI addon(node-addon-api)
|
|
23
|
+
├── aoq_node_ext.cc NODE_API_MODULE 入口
|
|
24
|
+
├── aoq_electron_bridge.* Napi::ObjectWrap 包装 AoqClientEngine 单例,
|
|
25
|
+
│ rapidjson 解析 JSON 参数组装 C++ 结构体
|
|
26
|
+
├── node_aoq_event_handler.* AoqEngineEventListener → ThreadSafeFunction
|
|
27
|
+
│ 跨线程投递 JS 主线程
|
|
28
|
+
└── node_aoq_frame_observers.* IAudioFrameObserver / IVideoFrameObserver
|
|
29
|
+
适配(meta JSON + PCM/I420 buffer 上抛)
|
|
30
|
+
│ 链接
|
|
31
|
+
▼
|
|
32
|
+
AoqClientSdk.framework 原生 SDK(api/Mac/AoqClientSdk 构建产物,
|
|
33
|
+
POST_BUILD 拷到 .node 同目录,@loader_path 解析)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 目录结构
|
|
37
|
+
|
|
38
|
+
| 路径 | 说明 |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `CMakeLists.txt` | cmake-js 工程;Darwin 链接 framework,Windows 分支预留 |
|
|
41
|
+
| `source_code/aoq_node_ext/` | NAPI addon C++ 源码 |
|
|
42
|
+
| `ts/` | TypeScript 源码(发布时经 tsc 编到 `js/` + `types/`) |
|
|
43
|
+
| `build/Release/` | 构建产物:`aoq_node_ext.node` + `AoqClientSdk.framework`(由 CMakeLists 的 `OUTPUT_DIRECTORY` 与 POST_BUILD 拷贝指定) |
|
|
44
|
+
| `dist/` | `npm pack` 产出的 tgz 二进制依赖包(Pack 模式) |
|
|
45
|
+
| `package.json` | npm 包定义,main=`js/AoqSdk`;`files` 字段决定 tgz 内容 |
|
|
46
|
+
|
|
47
|
+
## 构建
|
|
48
|
+
|
|
49
|
+
前置:需要一份 Mac 原生 framework,**支持三种来源**(按优先级):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 来源 1:指定本地目录(远程预编译产物已自行下载解压)
|
|
53
|
+
AOQ_MAC_SDK_DIR=/path/to/dir sh aoq-client-sdk/build_scripts/Electron/generate_aoq_sdk.sh Release
|
|
54
|
+
|
|
55
|
+
# 来源 2:远程 zip(内含 AoqClientSdk.framework,允许嵌套目录),
|
|
56
|
+
# 自动下载解压到 prebuilt/<Config>/ 缓存,Rebuild 时重下
|
|
57
|
+
AOQ_MAC_SDK_URL=https://xxx/AoqClientSdk.framework.zip \
|
|
58
|
+
sh aoq-client-sdk/build_scripts/Electron/generate_aoq_sdk.sh Release Build Pack
|
|
59
|
+
|
|
60
|
+
# 来源 3(默认):本地构建产物 api/Mac/AoqClientSdk/build/<Config>/
|
|
61
|
+
sh aoq-client-sdk/build_scripts/Electron/generate_aoq_sdk.sh [Debug|Release] [Build|Rebuild]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
适用场景:只改 Electron 桥/demo 或 CI 出包时用远程预编译产物(来源 1/2,
|
|
65
|
+
免去原生 SDK 全量编译且与发布二进制一致);改了 rtc_core 源码联调时用本地构建(来源 3)。
|
|
66
|
+
|
|
67
|
+
手动构建(在本目录):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install
|
|
71
|
+
npm run build_mac_debug # / build_mac_release,cmake-js 编 addon
|
|
72
|
+
# 指定外部 framework:npm run build_mac_release -- --CDAOQ_MAC_SDK_DIR=/path/to/dir
|
|
73
|
+
npm run buildJS # tsc 编 ts/ → js/ + types/
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
冒烟验证:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
node -e "const s=require('./js/AoqSdk');console.log(s.createAoqClientEngine().getVersion())"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API 自动化测试
|
|
83
|
+
|
|
84
|
+
对齐 iOS/Mac/Android/OHOS 自动化体系的 Jest 测试套件(17 个测试类 / 281 用例,
|
|
85
|
+
API 覆盖率 100%)。
|
|
86
|
+
|
|
87
|
+
### 运行方式
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 推荐:runner 脚本(产物检查 → 依赖安装 → 执行用例 → 覆盖率 + 多格式报告)
|
|
91
|
+
# ⚠️ 必须用 bash 执行:脚本用了进程替换等 bash 特性,用 sh 会报语法错误
|
|
92
|
+
bash aoq-client-sdk/build_scripts/test/run-automated-tests-electron.sh
|
|
93
|
+
|
|
94
|
+
# 或在本目录直接跑 Jest(不生成报告)
|
|
95
|
+
npm test # 全量
|
|
96
|
+
npx jest --runInBand test/AoqEngineConnect.test.js # 单个测试类
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> 脚本位于 `build_scripts/test/`,与其他平台的
|
|
100
|
+
> `run-automated-tests-{ios,android,ohos}.sh` 同目录。
|
|
101
|
+
|
|
102
|
+
### runner 参数
|
|
103
|
+
|
|
104
|
+
| 参数 | 说明 |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `-c ClassName` | 只跑指定测试类(如 `-c AoqEngineConnect`,可重复指定多个) |
|
|
107
|
+
| `-m Class/用例名` | 只跑单个用例,类名自动提取,无需再写 `-c`;可重复 |
|
|
108
|
+
| `-t "用例名"` | 按标题匹配用例(Jest `-t`,支持部分匹配) |
|
|
109
|
+
| `-o dir` | 输出目录,默认 `build_scripts/test/test-results-electron`(相对脚本所在目录,不是当前工作目录) |
|
|
110
|
+
| `--skip-build` | 跳过 SDK 构建(要求 `js/` 与 `build/Release` 产物已存在) |
|
|
111
|
+
| `--coverage` / `--skip-coverage` | 启用(默认已启用)/ 跳过覆盖率与报告生成 |
|
|
112
|
+
| `--no-pull` | 跳过编译前对 demo/sdk/libaoq 三仓的 git pull(默认开启;仅 main 分支拉取,`--skip-build` 时不拉) |
|
|
113
|
+
| `--no-retry` | 禁用失败 suite 的隔离重试 |
|
|
114
|
+
| `-h`, `--help` | 完整帮助 |
|
|
115
|
+
|
|
116
|
+
退出码:全部通过 `0`;存在失败/超时/崩溃 `1`。
|
|
117
|
+
|
|
118
|
+
示例:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 快速回归:只验证连接与生命周期,跳过构建与 git pull
|
|
122
|
+
bash .../run-automated-tests-electron.sh -c AoqEngineConnect -c AoqEngineLifecycle --skip-build
|
|
123
|
+
|
|
124
|
+
# 定位单个用例
|
|
125
|
+
bash .../run-automated-tests-electron.sh -m AoqEngineCallback/onConnectionStatusChange --skip-build
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 报告输出
|
|
129
|
+
|
|
130
|
+
默认写到 `build_scripts/test/test-results-electron/`(可用 `-o` 指定到其他位置;
|
|
131
|
+
脚本结尾会打印 Markdown/HTML 报告的绝对路径):
|
|
132
|
+
|
|
133
|
+
| 文件 | 内容 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `report.html` | 可视化 HTML 报告(与 iOS/Mac/Android 端同模板) |
|
|
136
|
+
| `summary.json` | 机器可读摘要(schema 与他端一致,供 CI 解析) |
|
|
137
|
+
| `report.md` | Markdown 报告:总览 + API 覆盖率 + 测试类汇总 + 失败详情 + 用例明细;可直接贴 PR / 语雀 / 钉钉 |
|
|
138
|
+
| `report.json` | 与 Markdown 同源的结构化明细 |
|
|
139
|
+
| `jest-results.json` | Jest 原始结果(含隔离重试的合并结果) |
|
|
140
|
+
| `retry-<Suite>.json` | 失败 suite 隔离重试的原始结果(仅发生重试时生成) |
|
|
141
|
+
| `repo-commits.txt` | 本次执行时三仓(demo/sdk/libaoq)的分支与 commit 溯源 |
|
|
142
|
+
| `test-run.log` | 完整执行日志(含 native SDK 全量输出,排查时看这个) |
|
|
143
|
+
| `logs/` | 崩溃诊断:`<Suite>_crash.log` + `crash_reports/*.ips` |
|
|
144
|
+
|
|
145
|
+
Markdown 报告结构示例:
|
|
146
|
+
|
|
147
|
+
```markdown
|
|
148
|
+
# AoqClientSdk Electron API 测试报告
|
|
149
|
+
> SDK 版本 `1.2.0.xxxx` · 生成时间 2026-08-03 11:20:00 · 平台 macOS(arm64)
|
|
150
|
+
|
|
151
|
+
## 总览
|
|
152
|
+
| 指标 | 值 |
|
|
153
|
+
| 结论 | **✅ 全部通过** |
|
|
154
|
+
| 用例总数 | 281 |
|
|
155
|
+
| API 覆盖率 | 100% (42/42) |
|
|
156
|
+
|
|
157
|
+
## 测试类汇总
|
|
158
|
+
| 测试类 | 结果 | 用例 | 通过 | 失败 | 耗时 |
|
|
159
|
+
| AoqEngineVideo | ✅ | 36 | 36 | 0 | 11.1s |
|
|
160
|
+
...
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
失败时自动插入「失败详情」章节(已清理 ANSI 色码,包含完整断言与调用栈)。
|
|
164
|
+
|
|
165
|
+
### 测试类清单
|
|
166
|
+
|
|
167
|
+
| 测试类 | 用例数 | 覆盖范围 |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `AoqEngineLifecycle` | 26 | getVersion / createEngine 参数矩阵 / 单例行为 / connect-disconnect 组合 |
|
|
170
|
+
| `AoqEngineConnect` | 25 | connect 参数校验(空值/越界 port)/ 状态机序列 / 生命周期混乱 |
|
|
171
|
+
| `AoqEnginePreCondition` | 15 | 未连接与未启动状态下的全 API 矩阵 + 状态完整性 |
|
|
172
|
+
| `AoqEngineCallback` | 12 | 连接状态/音频设备状态等回调的触发与字段校验 |
|
|
173
|
+
| `AoqEngineStats` | 6 | 统计回调(onStats)内容与频率 |
|
|
174
|
+
| `AoqEngineAudio` | 31 | 采集/播放启停、静音、编解码配置、设备状态 |
|
|
175
|
+
| `AoqEngineAudioFile` | 27 | 音频文件混音(启停/暂停/位置/音量/多文件) |
|
|
176
|
+
| `AoqEngineAudioStream` | 29 | 外部音频流(add/push/音量/clear/remove/多流) |
|
|
177
|
+
| `AoqEnginePlayerMixerNullPtr` | 5 | 播放器/混音器空指针保护 |
|
|
178
|
+
| `AoqEngineVideo` | 36 | 视频采集、编解码配置、外部推帧(BGRA/I420/JPEG) |
|
|
179
|
+
| `AoqEngineVideoCamera` | 8 | 摄像头控制与切换 |
|
|
180
|
+
| `AoqEngineVideoDecoderConfig` | 5 | 视频解码配置 |
|
|
181
|
+
| `AoqEngineVideoPipeline` | 7 | 视频管线组合场景 |
|
|
182
|
+
| `AoqEngineFrameObserver` | 12 | 音视频帧观察者注册/开关/帧数据与 stride 校验 |
|
|
183
|
+
| `AoqEngineTrackModeConfig` | 5 | trackMode 配置 |
|
|
184
|
+
| `AoqEngineDataMsg` | 19 | 数据消息参数矩阵、大载荷(64KB~8MB)、压力循环 |
|
|
185
|
+
| `AoqEngineDestroy` | 13 | destroy 幂等、销毁后 API 短路、重建可用性 |
|
|
186
|
+
|
|
187
|
+
设计要点(与 iOS 一致):
|
|
188
|
+
- **不连真实服务**:所有 connect 指向不可达端点 `127.0.0.1:9999`,只验证参数处理与 API 安全性
|
|
189
|
+
- **armed 隔离**:引擎为单例,MockDelegate 用 armed 开关丢弃前序残留回调
|
|
190
|
+
- **串行执行**:`--runInBand`,避免多 worker 抢音频/摄像头设备与 native 单例锁
|
|
191
|
+
- **环境降级**:无麦克风/摄像头权限时,帧回调类用例以 `got` 守护跳过断言,不误报
|
|
192
|
+
- **超时预算**:`callWithBudget` 对可能阻塞的接口测耗时,超预算以 `[SDK BUG]` 标记暴露
|
|
193
|
+
|
|
194
|
+
### 新增用例
|
|
195
|
+
|
|
196
|
+
在 `test/` 下新建 `<类名>.test.js` 即自动被发现(`testMatch: test/**/*.test.js`),
|
|
197
|
+
公共能力从 `test/helpers/TestHelper.js` 取:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
const { createEngineOrFail, makeConnectConfig, sleep, T } = require('./helpers/TestHelper')
|
|
201
|
+
|
|
202
|
+
describe('AoqEngineXxx', () => {
|
|
203
|
+
let engine, delegate
|
|
204
|
+
beforeEach(() => { ({ engine, delegate } = createEngineOrFail('xxx')) })
|
|
205
|
+
afterEach(async () => { engine.disconnect(); await sleep(300); delegate.uninstall() })
|
|
206
|
+
|
|
207
|
+
test('某接口返回 0', () => {
|
|
208
|
+
expect(engine.someApi({ /* ... */ })).toBe(0)
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
新增用例后重跑 runner,覆盖率会自动重算(静态扫描 `engine.<method>(` 调用)。
|
|
214
|
+
|
|
215
|
+
## 依赖引入方式(两种均支持)
|
|
216
|
+
|
|
217
|
+
**方式一:源码目录(file: 符号链接,仓内联调默认)**
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
"dependencies": { "aoq-electron-sdk": "file:../../aoq-client-sdk/api/electron/AoqClientSdk" }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
npm 创建符号链接指向本目录,重编 SDK 后消费方无需重新 install 即生效。
|
|
224
|
+
|
|
225
|
+
**方式二:二进制包(npm tgz,发布形态 / 外部工程集成)**
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# 打包:构建 + npm pack → dist/aoq-electron-sdk-<ver>.tgz
|
|
229
|
+
sh aoq-client-sdk/build_scripts/Electron/generate_aoq_sdk.sh Release Build Pack
|
|
230
|
+
|
|
231
|
+
# 消费方安装(真实拷贝进 node_modules)
|
|
232
|
+
npm install /path/to/aoq-electron-sdk-1.0.0.tgz
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
注意:`.framework` 内含符号链接而 npm pack 会丢弃 symlink,故打包时用
|
|
236
|
+
ditto 压成 `AoqClientSdk.framework.zip` 随包分发(`files` 已排除 framework
|
|
237
|
+
目录本体),安装时由 `scripts/postinstall.js` 自动解压还原。
|
|
238
|
+
|
|
239
|
+
demo 工程可用 `aoq-client-demo/Electron/use_sdk.sh source|package` 一键切换。
|
|
240
|
+
|
|
241
|
+
## 发布到 npm 仓库(外部客户 npm install 直接下载)
|
|
242
|
+
|
|
243
|
+
### 一次性准备(首次发布前)
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# 1. 登录 npm 账号(浏览器完成授权,建议开启 2FA)
|
|
247
|
+
npm login --registry https://registry.npmjs.org/
|
|
248
|
+
|
|
249
|
+
# 2. 检查包名是否被占用(404 = 可用)
|
|
250
|
+
npm view aoq-electron-sdk --registry https://registry.npmjs.org/
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
包名已定为 **`aoq-electron-sdk`**(裸名,对齐 agora-electron-sdk 的命名风格)。
|
|
254
|
+
裸名在 npm 上先到先得,建议尽早完成首次发布占住名字;若 `npm view` 发现
|
|
255
|
+
已被占用,再考虑切换 scoped 形式(改 package.json 的 `name` 字段并同步
|
|
256
|
+
demo 侧 import/配置即可)。`publishConfig.access` 保持 `public`(对裸名包
|
|
257
|
+
无副作用,未来切 scoped 时免踩 private 默认值)。
|
|
258
|
+
|
|
259
|
+
### 每次发布流程
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# 1. 构建 Release 产物 + 生成 framework.zip(必须走 Pack)
|
|
263
|
+
sh aoq-client-sdk/build_scripts/Electron/generate_aoq_sdk.sh Release Build Pack
|
|
264
|
+
|
|
265
|
+
# 2. 升版本号(npm 不允许覆盖已发布的版本)
|
|
266
|
+
cd aoq-client-sdk/api/electron/AoqClientSdk
|
|
267
|
+
npm version patch # 或 minor / major,自动改 package.json
|
|
268
|
+
|
|
269
|
+
# 3. 发布(自动触发 prepublishOnly 产物校验)
|
|
270
|
+
npm publish
|
|
271
|
+
|
|
272
|
+
# 4. 验证
|
|
273
|
+
npm view aoq-electron-sdk
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
客户侧从此可直接:`npm install aoq-electron-sdk`。
|
|
277
|
+
|
|
278
|
+
发布前可用 `npm publish --dry-run` 预检(不上传,展示包内容与体积)。
|
|
279
|
+
|
|
280
|
+
### 已内置的发布防护(package.json)
|
|
281
|
+
|
|
282
|
+
| 配置 | 作用 |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `prepublishOnly` → `scripts/prepublish-check.js` | 发布前强制校验 `js/`、`types/`、`.node`、`framework.zip` 四类产物齐全,防止忘跑 Pack 发出缺原生库的废包 |
|
|
285
|
+
| `os: ["darwin"]` / `cpu: ["arm64"]` | Windows/Linux/x64 用户 install 时直接报平台不支持,而非运行时崩溃 |
|
|
286
|
+
| `publishConfig.registry/access` | 锁定发布到 registry.npmjs.org;access 显式 public,未来切 scoped 包名时免踩 private 默认值 |
|
|
287
|
+
|
|
288
|
+
### 注意事项
|
|
289
|
+
|
|
290
|
+
- **合规**:framework 是完整原生 SDK 二进制,发到公网 npm 意味着任何人可
|
|
291
|
+
下载(含符号信息),发布前需确认商务/安全侧允许对外分发。
|
|
292
|
+
- **CI 发布**:用 npm Access Token(`npm token create` → CI 环境变量
|
|
293
|
+
`NPM_TOKEN`,`.npmrc` 写 `//registry.npmjs.org/:_authToken=${NPM_TOKEN}`),
|
|
294
|
+
避免人肉 login。
|
|
295
|
+
- **发布目标**:本包面向公网所有用户,固定发布到 registry.npmjs.org(已由
|
|
296
|
+
`publishConfig.registry` 锁定),不使用内部 registry。
|
|
297
|
+
- **版本不可覆盖**:发错了只能 `npm deprecate` 标废 + 发新版本,
|
|
298
|
+
`npm unpublish` 有 72 小时等严格限制,发布前务必 dry-run 确认。
|
|
299
|
+
|
|
300
|
+
## 使用
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
import createAoqClientEngine, { AoqTrackType } from 'aoq-electron-sdk'
|
|
304
|
+
|
|
305
|
+
const engine = createAoqClientEngine()
|
|
306
|
+
engine.on('onConnectionStatusChange', (status) => console.log(status))
|
|
307
|
+
engine.on('onDataMsg', (data) => console.log(Buffer.from(data).toString()))
|
|
308
|
+
|
|
309
|
+
engine.createEngine({ workDir: '/tmp/aoq', enableDumpAudio: false, extras: '' })
|
|
310
|
+
engine.connect({
|
|
311
|
+
token, sid, certFingerprint,
|
|
312
|
+
relayEndpoints: [{ routeIndex: 0, endpoint, port }],
|
|
313
|
+
publishTracks: [{ trackType: AoqTrackType.AoqTrackTypeAudio }],
|
|
314
|
+
subscribeTracks: [{ trackType: AoqTrackType.AoqTrackTypeAudio }]
|
|
315
|
+
})
|
|
316
|
+
engine.startAudioCapture({ channel: 1 })
|
|
317
|
+
engine.startAudioPlayer({ channel: 1 })
|
|
318
|
+
engine.sendDataMsg('hello')
|
|
319
|
+
engine.disconnect()
|
|
320
|
+
engine.destroy()
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Electron 集成注意(renderer 直接加载模式):
|
|
324
|
+
- `nodeIntegration: true`,webpack `externals: {'aoq-electron-sdk': 'commonjs2 aoq-electron-sdk'}`
|
|
325
|
+
- 生产应用建议改走 main 进程 + IPC
|
|
326
|
+
|
|
327
|
+
## API 面(与 AoqClientEngine.h 对齐)
|
|
328
|
+
|
|
329
|
+
- 生命周期/连接:`createEngine / destroy / getVersion / connect / disconnect`
|
|
330
|
+
- 音频:采集/播放/静音/暂停/恢复/打断、`setAudioEncoderConfig / setAudioDecoderConfig`
|
|
331
|
+
- 视频:`startVideoCapture / stopVideoCapture`、`setVideoEncoder/DecoderConfig`、
|
|
332
|
+
`pushExternalVideoCapturedFrame(BGRA/I420)`、`pushExternalVideoEncodedFrame(JPEG)`
|
|
333
|
+
- 音频文件混音:start/stop/pause/resume/时长/位置/音量 全套
|
|
334
|
+
- 外部音频流:add/remove/push(PCM buffer)/音量/clearBuffer
|
|
335
|
+
- 帧观察者:`setAudioFrameObserver / enableAudioFrameObserver`、
|
|
336
|
+
`setVideoFrameObserver / enableVideoFrameObserver`(仅只读模式)
|
|
337
|
+
- 数据消息/流控制:`sendDataMsg / enableSendMediaStream`
|
|
338
|
+
|
|
339
|
+
事件(`engine.on(...)`):`onError / onWarning / onConnectionStatusChange / onStats /
|
|
340
|
+
onAudioDeviceStateChanged / onAudioDeviceRouteChanged / onAudioFileState /
|
|
341
|
+
onVideoDeviceStateChanged / onDataMsg` + 帧观察者
|
|
342
|
+
`onCaptured/ProcessCaptured/Publish/PlaybackAudioFrame`、
|
|
343
|
+
`onCaptured/PreEncode/RemoteVideoFrame`。
|
|
344
|
+
|
|
345
|
+
## 视频渲染方案
|
|
346
|
+
|
|
347
|
+
Electron renderer 是 Chromium,无法嵌入原生 view,故不透传
|
|
348
|
+
`setLocalView/setRemoteView`。预览/渲染统一走:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
enableVideoFrameObserver(I420) → 帧事件(meta + Y/U/V 拼接 buffer)
|
|
352
|
+
→ YUVCanvasRenderer.drawFrame(canvas)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`ts/Renderer/YUVCanvasRenderer.ts` 基于 yuv-buffer + yuv-canvas 软件渲染,
|
|
356
|
+
渲染模式(auto/stretch/fill/crop)由调用方用 CSS object-fit 近似。
|
|
357
|
+
|
|
358
|
+
## 平台支持
|
|
359
|
+
|
|
360
|
+
| 平台 | 状态 |
|
|
361
|
+
|---|---|
|
|
362
|
+
| macOS (arm64) | 已支持,链接 `AoqClientSdk.framework` |
|
|
363
|
+
| Windows | 预留(CMakeLists Windows 分支 / 脚本注释),等原生 SDK Windows 构建链就绪后填入 |
|
|
364
|
+
|
|
365
|
+
## 约束
|
|
366
|
+
|
|
367
|
+
- 帧观察者仅支持只读(ReadWrite 跨进程回写不支持);音频观察者 mode 固定 ReadOnly
|
|
368
|
+
- CVPixelBuffer 外部推帧为 Apple 平台专属,Electron 侧提供 BGRA/I420/JPEG 路径
|
|
369
|
+
- native 回调经 TSFN 异步投递,事件处理内请勿做重计算,避免高频帧事件堆积
|
|
Binary file
|
|
Binary file
|
package/js/AoqSdk.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createAoqClientEngine = createAoqClientEngine;
|
|
18
|
+
const AoqClientEngineInternal_1 = require("./internal/AoqClientEngineInternal");
|
|
19
|
+
__exportStar(require("./IAoqClientEngine"), exports);
|
|
20
|
+
__exportStar(require("./Renderer/YUVCanvasRenderer"), exports);
|
|
21
|
+
__exportStar(require("./Types"), exports);
|
|
22
|
+
let instance;
|
|
23
|
+
/**
|
|
24
|
+
* 获取引擎包装实例(懒加载单例,与 native AoqClientEngine 单例语义对齐)。
|
|
25
|
+
*/
|
|
26
|
+
function createAoqClientEngine() {
|
|
27
|
+
if (instance === undefined) {
|
|
28
|
+
instance = new AoqClientEngineInternal_1.AoqClientEngineInternal();
|
|
29
|
+
}
|
|
30
|
+
return instance;
|
|
31
|
+
}
|
|
32
|
+
exports.default = createAoqClientEngine;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.YUVCanvasRenderer = void 0;
|
|
4
|
+
/*
|
|
5
|
+
* YUVCanvasRenderer — 将视频帧观察者事件的 I420 数据画到 HTMLCanvasElement。
|
|
6
|
+
*
|
|
7
|
+
* Electron 没有原生 view,本渲染器承担 Mac 端 setLocalView/setRemoteView 的
|
|
8
|
+
* 预览职责:页面把 canvas 绑定到某个帧回调位置(captured/remote),
|
|
9
|
+
* 每次帧事件调用 drawFrame 即可。软件渲染基于 yuv-buffer + yuv-canvas。
|
|
10
|
+
*/
|
|
11
|
+
const Types_1 = require("../Types");
|
|
12
|
+
/* yuv-canvas 依赖 DOM,延迟到首次 bind 时再 require,
|
|
13
|
+
* 避免在无 DOM 的 node/main 进程里加载 SDK 入口即失败。 */
|
|
14
|
+
let YUVBuffer;
|
|
15
|
+
let YUVCanvas;
|
|
16
|
+
function ensureYuvLibs() {
|
|
17
|
+
if (!YUVBuffer) {
|
|
18
|
+
YUVBuffer = require('yuv-buffer');
|
|
19
|
+
YUVCanvas = require('yuv-canvas');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
class YUVCanvasRenderer {
|
|
23
|
+
/** 绑定 canvas(重复绑定会替换 sink) */
|
|
24
|
+
bind(canvas) {
|
|
25
|
+
ensureYuvLibs();
|
|
26
|
+
this.canvas = canvas;
|
|
27
|
+
this.frameSink = YUVCanvas.attach(canvas, { webGL: false });
|
|
28
|
+
}
|
|
29
|
+
/** 解绑并清空画面 */
|
|
30
|
+
unbind() {
|
|
31
|
+
if (this.canvas) {
|
|
32
|
+
const ctx = this.canvas.getContext('2d');
|
|
33
|
+
ctx?.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
34
|
+
}
|
|
35
|
+
this.frameSink = undefined;
|
|
36
|
+
this.canvas = undefined;
|
|
37
|
+
}
|
|
38
|
+
get bound() {
|
|
39
|
+
return this.frameSink !== undefined;
|
|
40
|
+
}
|
|
41
|
+
/** 绘制一帧(仅支持 I420;buffer 为 Y/U/V 平面按 stride 拼接) */
|
|
42
|
+
drawFrame(frame) {
|
|
43
|
+
if (!this.frameSink)
|
|
44
|
+
return;
|
|
45
|
+
if (frame.format !== Types_1.AoqVideoPixelFormat.AoqVideoPixelFormatI420)
|
|
46
|
+
return;
|
|
47
|
+
if (!frame.width || !frame.height || !frame.buffer?.length)
|
|
48
|
+
return;
|
|
49
|
+
const ySize = frame.strideY * frame.height;
|
|
50
|
+
const uSize = frame.strideU * (frame.height / 2);
|
|
51
|
+
const vSize = frame.strideV * (frame.height / 2);
|
|
52
|
+
if (frame.buffer.length < ySize + uSize + vSize)
|
|
53
|
+
return;
|
|
54
|
+
const format = YUVBuffer.format({
|
|
55
|
+
width: frame.width,
|
|
56
|
+
height: frame.height,
|
|
57
|
+
chromaWidth: frame.width / 2,
|
|
58
|
+
chromaHeight: frame.height / 2,
|
|
59
|
+
});
|
|
60
|
+
const yuvFrame = YUVBuffer.frame(format, { bytes: frame.buffer.subarray(0, ySize), stride: frame.strideY }, { bytes: frame.buffer.subarray(ySize, ySize + uSize), stride: frame.strideU }, {
|
|
61
|
+
bytes: frame.buffer.subarray(ySize + uSize, ySize + uSize + vSize),
|
|
62
|
+
stride: frame.strideV,
|
|
63
|
+
});
|
|
64
|
+
this.frameSink.drawFrame(yuvFrame);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.YUVCanvasRenderer = YUVCanvasRenderer;
|
package/js/Types.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Types.ts — 枚举与配置结构定义。
|
|
4
|
+
*
|
|
5
|
+
* 字段名与 rtc_core/biz_engine/api/cpp_engine_api/AoqClientEngine.h
|
|
6
|
+
* 中对应结构体成员保持一致;本文件同时是 TS <-> native 之间
|
|
7
|
+
* JSON 协议的唯一事实来源。
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.AoqVideoCodecType = exports.AoqOrientationMode = exports.AoqMirrorMode = exports.AoqVideoObserverAlignment = exports.AoqVideoPixelFormat = exports.AoqVideoSource = exports.AoqAudioSource = exports.AoqAudioStreamDirection = exports.AoqVideoDeviceStateCode = exports.AoqAudioFileStateCode = exports.AoqAudioDeviceStateCode = exports.AoqConnectionStatus = exports.AoqEncoderType = exports.AoqTrackMode = exports.AoqTrackType = void 0;
|
|
11
|
+
/** track 媒体类型,对齐 AoqTrackType */
|
|
12
|
+
var AoqTrackType;
|
|
13
|
+
(function (AoqTrackType) {
|
|
14
|
+
AoqTrackType[AoqTrackType["AoqTrackTypeAudio"] = 0] = "AoqTrackTypeAudio";
|
|
15
|
+
AoqTrackType[AoqTrackType["AoqTrackTypeVideo"] = 1] = "AoqTrackTypeVideo";
|
|
16
|
+
AoqTrackType[AoqTrackType["AoqTrackTypeData"] = 2] = "AoqTrackTypeData";
|
|
17
|
+
AoqTrackType[AoqTrackType["AoqTrackTypeScreen"] = 3] = "AoqTrackTypeScreen";
|
|
18
|
+
})(AoqTrackType || (exports.AoqTrackType = AoqTrackType = {}));
|
|
19
|
+
/** 流式与非流式模式(仅音频下行生效),对齐 AoqTrackMode */
|
|
20
|
+
var AoqTrackMode;
|
|
21
|
+
(function (AoqTrackMode) {
|
|
22
|
+
AoqTrackMode[AoqTrackMode["AoqTrackModeSegment"] = 0] = "AoqTrackModeSegment";
|
|
23
|
+
AoqTrackMode[AoqTrackMode["AoqTrackModeStream"] = 1] = "AoqTrackModeStream";
|
|
24
|
+
})(AoqTrackMode || (exports.AoqTrackMode = AoqTrackMode = {}));
|
|
25
|
+
/** track 媒体格式,对齐 AoqEncoderType */
|
|
26
|
+
var AoqEncoderType;
|
|
27
|
+
(function (AoqEncoderType) {
|
|
28
|
+
AoqEncoderType[AoqEncoderType["AoqEncoderTypeUnknown"] = 0] = "AoqEncoderTypeUnknown";
|
|
29
|
+
AoqEncoderType[AoqEncoderType["AoqEncoderTypeAudioPCM"] = 1] = "AoqEncoderTypeAudioPCM";
|
|
30
|
+
AoqEncoderType[AoqEncoderType["AoqEncoderTypeAudioOpus"] = 2] = "AoqEncoderTypeAudioOpus";
|
|
31
|
+
AoqEncoderType[AoqEncoderType["AoqEncoderTypeVideoH264"] = 3] = "AoqEncoderTypeVideoH264";
|
|
32
|
+
AoqEncoderType[AoqEncoderType["AoqEncoderTypeVideoJpeg"] = 4] = "AoqEncoderTypeVideoJpeg";
|
|
33
|
+
AoqEncoderType[AoqEncoderType["AoqEncoderTypeDataText"] = 5] = "AoqEncoderTypeDataText";
|
|
34
|
+
})(AoqEncoderType || (exports.AoqEncoderType = AoqEncoderType = {}));
|
|
35
|
+
/** 引擎连接状态,对齐 AoqConnectionStatus */
|
|
36
|
+
var AoqConnectionStatus;
|
|
37
|
+
(function (AoqConnectionStatus) {
|
|
38
|
+
AoqConnectionStatus[AoqConnectionStatus["AoqConnectionStatusDisconnected"] = 0] = "AoqConnectionStatusDisconnected";
|
|
39
|
+
AoqConnectionStatus[AoqConnectionStatus["AoqConnectionStatusConnecting"] = 1] = "AoqConnectionStatusConnecting";
|
|
40
|
+
AoqConnectionStatus[AoqConnectionStatus["AoqConnectionStatusConnected"] = 2] = "AoqConnectionStatusConnected";
|
|
41
|
+
AoqConnectionStatus[AoqConnectionStatus["AoqConnectionStatusFailed"] = 3] = "AoqConnectionStatusFailed";
|
|
42
|
+
})(AoqConnectionStatus || (exports.AoqConnectionStatus = AoqConnectionStatus = {}));
|
|
43
|
+
/** 音频设备状态码,对齐 AoqAudioDeviceStateCode */
|
|
44
|
+
var AoqAudioDeviceStateCode;
|
|
45
|
+
(function (AoqAudioDeviceStateCode) {
|
|
46
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDeviceNone"] = 0] = "AoqAudioDeviceNone";
|
|
47
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDeviceRecordStarting"] = 1] = "AoqAudioDeviceRecordStarting";
|
|
48
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDeviceRecordStarted"] = 2] = "AoqAudioDeviceRecordStarted";
|
|
49
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDeviceRecordStopping"] = 3] = "AoqAudioDeviceRecordStopping";
|
|
50
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDeviceRecordStopped"] = 4] = "AoqAudioDeviceRecordStopped";
|
|
51
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDeviceRecordFail"] = 5] = "AoqAudioDeviceRecordFail";
|
|
52
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDevicePlayStarting"] = 6] = "AoqAudioDevicePlayStarting";
|
|
53
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDevicePlayStarted"] = 7] = "AoqAudioDevicePlayStarted";
|
|
54
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDevicePlayStopping"] = 8] = "AoqAudioDevicePlayStopping";
|
|
55
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDevicePlayStopped"] = 9] = "AoqAudioDevicePlayStopped";
|
|
56
|
+
AoqAudioDeviceStateCode[AoqAudioDeviceStateCode["AoqAudioDevicePlayFail"] = 10] = "AoqAudioDevicePlayFail";
|
|
57
|
+
})(AoqAudioDeviceStateCode || (exports.AoqAudioDeviceStateCode = AoqAudioDeviceStateCode = {}));
|
|
58
|
+
/** 音频文件播放状态码,对齐 AoqAudioFileStateCode */
|
|
59
|
+
var AoqAudioFileStateCode;
|
|
60
|
+
(function (AoqAudioFileStateCode) {
|
|
61
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileNone"] = 0] = "AoqAudioFileNone";
|
|
62
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileStarted"] = 1] = "AoqAudioFileStarted";
|
|
63
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileStopped"] = 2] = "AoqAudioFileStopped";
|
|
64
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFilePaused"] = 3] = "AoqAudioFilePaused";
|
|
65
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileResumed"] = 4] = "AoqAudioFileResumed";
|
|
66
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileEnded"] = 5] = "AoqAudioFileEnded";
|
|
67
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileBuffering"] = 6] = "AoqAudioFileBuffering";
|
|
68
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileBufferingEnd"] = 7] = "AoqAudioFileBufferingEnd";
|
|
69
|
+
AoqAudioFileStateCode[AoqAudioFileStateCode["AoqAudioFileFailed"] = 8] = "AoqAudioFileFailed";
|
|
70
|
+
})(AoqAudioFileStateCode || (exports.AoqAudioFileStateCode = AoqAudioFileStateCode = {}));
|
|
71
|
+
/** 视频设备状态码,对齐 AoqVideoDeviceStateCode */
|
|
72
|
+
var AoqVideoDeviceStateCode;
|
|
73
|
+
(function (AoqVideoDeviceStateCode) {
|
|
74
|
+
AoqVideoDeviceStateCode[AoqVideoDeviceStateCode["AoqVideoDeviceNone"] = 0] = "AoqVideoDeviceNone";
|
|
75
|
+
AoqVideoDeviceStateCode[AoqVideoDeviceStateCode["AoqVideoDeviceCaptureStarting"] = 1] = "AoqVideoDeviceCaptureStarting";
|
|
76
|
+
AoqVideoDeviceStateCode[AoqVideoDeviceStateCode["AoqVideoDeviceCaptureStarted"] = 2] = "AoqVideoDeviceCaptureStarted";
|
|
77
|
+
AoqVideoDeviceStateCode[AoqVideoDeviceStateCode["AoqVideoDeviceCaptureStopping"] = 3] = "AoqVideoDeviceCaptureStopping";
|
|
78
|
+
AoqVideoDeviceStateCode[AoqVideoDeviceStateCode["AoqVideoDeviceCaptureStopped"] = 4] = "AoqVideoDeviceCaptureStopped";
|
|
79
|
+
AoqVideoDeviceStateCode[AoqVideoDeviceStateCode["AoqVideoDeviceCaptureFail"] = 5] = "AoqVideoDeviceCaptureFail";
|
|
80
|
+
})(AoqVideoDeviceStateCode || (exports.AoqVideoDeviceStateCode = AoqVideoDeviceStateCode = {}));
|
|
81
|
+
/** 音频流方向,对齐 AoqAudioStreamDirection */
|
|
82
|
+
var AoqAudioStreamDirection;
|
|
83
|
+
(function (AoqAudioStreamDirection) {
|
|
84
|
+
AoqAudioStreamDirection[AoqAudioStreamDirection["AoqAudioStreamPublish"] = 0] = "AoqAudioStreamPublish";
|
|
85
|
+
AoqAudioStreamDirection[AoqAudioStreamDirection["AoqAudioStreamPlayout"] = 1] = "AoqAudioStreamPlayout";
|
|
86
|
+
})(AoqAudioStreamDirection || (exports.AoqAudioStreamDirection = AoqAudioStreamDirection = {}));
|
|
87
|
+
/** 音频数据源类型,对齐 AoqAudioSource */
|
|
88
|
+
var AoqAudioSource;
|
|
89
|
+
(function (AoqAudioSource) {
|
|
90
|
+
AoqAudioSource[AoqAudioSource["AoqAudioSourceCaptured"] = 0] = "AoqAudioSourceCaptured";
|
|
91
|
+
AoqAudioSource[AoqAudioSource["AoqAudioSourceProcessCaptured"] = 1] = "AoqAudioSourceProcessCaptured";
|
|
92
|
+
AoqAudioSource[AoqAudioSource["AoqAudioSourcePublish"] = 2] = "AoqAudioSourcePublish";
|
|
93
|
+
AoqAudioSource[AoqAudioSource["AoqAudioSourcePlayback"] = 3] = "AoqAudioSourcePlayback";
|
|
94
|
+
})(AoqAudioSource || (exports.AoqAudioSource = AoqAudioSource = {}));
|
|
95
|
+
/** 视频帧回调数据源(管线位置),对齐 AoqVideoSource */
|
|
96
|
+
var AoqVideoSource;
|
|
97
|
+
(function (AoqVideoSource) {
|
|
98
|
+
AoqVideoSource[AoqVideoSource["AoqVideoSourceCaptured"] = 0] = "AoqVideoSourceCaptured";
|
|
99
|
+
AoqVideoSource[AoqVideoSource["AoqVideoSourcePreEncode"] = 1] = "AoqVideoSourcePreEncode";
|
|
100
|
+
AoqVideoSource[AoqVideoSource["AoqVideoSourceRemote"] = 2] = "AoqVideoSourceRemote";
|
|
101
|
+
})(AoqVideoSource || (exports.AoqVideoSource = AoqVideoSource = {}));
|
|
102
|
+
/** 视频像素格式,对齐 AoqVideoPixelFormat */
|
|
103
|
+
var AoqVideoPixelFormat;
|
|
104
|
+
(function (AoqVideoPixelFormat) {
|
|
105
|
+
AoqVideoPixelFormat[AoqVideoPixelFormat["AoqVideoPixelFormatUnknown"] = 0] = "AoqVideoPixelFormatUnknown";
|
|
106
|
+
AoqVideoPixelFormat[AoqVideoPixelFormat["AoqVideoPixelFormatI420"] = 1] = "AoqVideoPixelFormatI420";
|
|
107
|
+
AoqVideoPixelFormat[AoqVideoPixelFormat["AoqVideoPixelFormatNV12"] = 2] = "AoqVideoPixelFormatNV12";
|
|
108
|
+
AoqVideoPixelFormat[AoqVideoPixelFormat["AoqVideoPixelFormatNV21"] = 3] = "AoqVideoPixelFormatNV21";
|
|
109
|
+
AoqVideoPixelFormat[AoqVideoPixelFormat["AoqVideoPixelFormatBGRA"] = 4] = "AoqVideoPixelFormatBGRA";
|
|
110
|
+
AoqVideoPixelFormat[AoqVideoPixelFormat["AoqVideoPixelFormatRGBA"] = 5] = "AoqVideoPixelFormatRGBA";
|
|
111
|
+
})(AoqVideoPixelFormat || (exports.AoqVideoPixelFormat = AoqVideoPixelFormat = {}));
|
|
112
|
+
/** 视频宽度对齐方式,对齐 AoqVideoObserverAlignment */
|
|
113
|
+
var AoqVideoObserverAlignment;
|
|
114
|
+
(function (AoqVideoObserverAlignment) {
|
|
115
|
+
AoqVideoObserverAlignment[AoqVideoObserverAlignment["AoqVideoObserverAlignmentDefault"] = 0] = "AoqVideoObserverAlignmentDefault";
|
|
116
|
+
AoqVideoObserverAlignment[AoqVideoObserverAlignment["AoqVideoObserverAlignmentEven"] = 1] = "AoqVideoObserverAlignmentEven";
|
|
117
|
+
AoqVideoObserverAlignment[AoqVideoObserverAlignment["AoqVideoObserverAlignment4"] = 2] = "AoqVideoObserverAlignment4";
|
|
118
|
+
AoqVideoObserverAlignment[AoqVideoObserverAlignment["AoqVideoObserverAlignment8"] = 3] = "AoqVideoObserverAlignment8";
|
|
119
|
+
AoqVideoObserverAlignment[AoqVideoObserverAlignment["AoqVideoObserverAlignment16"] = 4] = "AoqVideoObserverAlignment16";
|
|
120
|
+
})(AoqVideoObserverAlignment || (exports.AoqVideoObserverAlignment = AoqVideoObserverAlignment = {}));
|
|
121
|
+
/** 镜像模式,对齐 AoqMirrorMode */
|
|
122
|
+
var AoqMirrorMode;
|
|
123
|
+
(function (AoqMirrorMode) {
|
|
124
|
+
AoqMirrorMode[AoqMirrorMode["AoqMirrorModeDisabled"] = 0] = "AoqMirrorModeDisabled";
|
|
125
|
+
AoqMirrorMode[AoqMirrorMode["AoqMirrorModeEnabled"] = 1] = "AoqMirrorModeEnabled";
|
|
126
|
+
})(AoqMirrorMode || (exports.AoqMirrorMode = AoqMirrorMode = {}));
|
|
127
|
+
/** 方向模式,对齐 AoqOrientationMode */
|
|
128
|
+
var AoqOrientationMode;
|
|
129
|
+
(function (AoqOrientationMode) {
|
|
130
|
+
AoqOrientationMode[AoqOrientationMode["AoqOrientationModeAuto"] = 0] = "AoqOrientationModeAuto";
|
|
131
|
+
AoqOrientationMode[AoqOrientationMode["AoqOrientationModePortrait"] = 1] = "AoqOrientationModePortrait";
|
|
132
|
+
AoqOrientationMode[AoqOrientationMode["AoqOrientationModeLandscape"] = 2] = "AoqOrientationModeLandscape";
|
|
133
|
+
})(AoqOrientationMode || (exports.AoqOrientationMode = AoqOrientationMode = {}));
|
|
134
|
+
/** 外部编码帧 codec,对齐 AoqVideoCodecType */
|
|
135
|
+
var AoqVideoCodecType;
|
|
136
|
+
(function (AoqVideoCodecType) {
|
|
137
|
+
AoqVideoCodecType[AoqVideoCodecType["AoqVideoCodecTypeJPEG"] = 0] = "AoqVideoCodecTypeJPEG";
|
|
138
|
+
})(AoqVideoCodecType || (exports.AoqVideoCodecType = AoqVideoCodecType = {}));
|