Qwen-Omni-Realtime AOQ 接入
AOQ 将音视频媒体与模型事件分轨传输。SDK 负责媒体采集、编解码、传输和播放;应用通过 Data 轨发送模型事件并处理回复。本页使用 Qwen-Omni-Realtime 的媒体方向和事件协议。
适用范围
本文面向 Qwen-Omni-Realtime。现有接入说明覆盖:qwen3.8-omni-flash-realtime、qwen3.5-omni-plus-realtime、qwen3.5-omni-flash-realtime。示例模型为 qwen3.8-omni-flash-realtime。
-
使用所选地域和业务空间对应的 API Key 与 Workspace ID。
-
北京域名:
{WorkspaceId}.cn-beijing.maas.aliyuncs.com;新加坡域名:{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com。 -
API Key 只保存在服务端,不放入浏览器或移动端应用。
-
使用麦克风采集音频或使用摄像头采集画面时,按平台要求申请相应权限。
-
从 SDK 下载获取对应平台的 SDK。
鉴权与连接凭证
说明如需提前获取 Token,或了解完整的鉴权流程,请参见建连鉴权详解。
由业务 AppServer 发起以下请求。请先配置 API Key 到环境变量,并将以下域名中的 {WorkspaceId} 替换为实际业务空间 ID;其他地域使用上文对应的域名。clientIp 选填,不填时使用请求网关的 IP。
参数说明
endpoint指定对应的接入域名,详情请参见选择地域、服务部署范围和接入域名。如果使用Token Plan,接入域名中的 WorkspaceId 固定为 token-plan。
Content-Type指定消息类型
Authorization填写API Key
x-dashscope-rtc-transport指定使用 AOQ 协议
clientIp选填。不填写时,默认使用请求百炼网关的 IP;填写后以 clientIp 为准。服务 会根据客户端 IP 分配最佳的 Relay 接入点
export ENDPOINT="{WorkspaceId}.cn-beijing.maas.aliyuncs.com"
curl -X POST \
"https://${ENDPOINT}/api/v1/webrtc/realtime?model=qwen3.8-omni-flash-realtime" \
-H "Authorization: Bearer ${DASHSCOPE_API_KEY}" \
-H "Content-Type: application/json" \
-H "x-dashscope-rtc-transport: moq" \
-d '{}'
如需指定客户端公网 IP,将请求体中的 -d '{}' 替换为 -d '{"clientIp":"<客户端公网 IP>"}'。
响应参数说明
sid会话唯一标识
aoqTokenForClient客户端连接令牌,传给 SDK 的 token 字段
clientRelayEndpointsRelay 接入点数组。响应示例中的数组元素包含以下字段:
属性
endpointRelay 接入点地址。
portRelay 接入点端口。
route_index下方 iOS 示例在该字段缺省时使用数组下标。
clientRelayCertFingerprintRelay TLS 证书指纹
sidExpiresInSecs会话过期时间(秒)
extraInfo附加信息。
属性
workspaceIdHash工作区 ID 哈希。
响应示例
{
"sid": "1d06b55683db49bba67a407902f62d02:1782706970:69aecdc5...",
"aoqTokenForClient": "ecc1a46015d5496ca4ff7a48281eb739",
"clientRelayEndpoints": [{"endpoint": "121.199.XX.XX", "port": 8443, "route_index": 0}],
"clientRelayCertFingerprint": "sha256/99843495...",
"sidExpiresInSecs": 7200,
"extraInfo": {"workspaceIdHash": "2021b6f98cea4cff"}
}
AOQ Client SDK 连接示例
说明clientIp 为请求体中的选填字段。不填写时,默认使用请求百炼网关的 IP 作为客户端 IP;填写后则以指定的 clientIp 为准。建议由业务 AppServer 获取客户端真实 IP 后填入,以获得最佳的 Relay 接入点。
let resp = try JSONDecoder().decode(AllocateResponse.self, from: responseData)
let config = AoqConnectConfig()
config.token = resp.aoqTokenForClient
config.sid = resp.sid
config.certFingerprint = resp.clientRelayCertFingerprint
config.relayEndpoints = resp.clientRelayEndpoints.enumerated().map { index, item in
let ep = AoqRelayEndpoint()
// route_index 缺省时按数组下标兜底
ep.routeIndex = item.routeIndex ?? index
ep.endpoint = item.endpoint
ep.port = item.port
return ep
}
config.workspaceIdHash = resp.extraInfo?.workspaceIdHash ?? ""
let audioTrack = AoqTrackParam()
audioTrack.trackType = .audio
let dataTrack = AoqTrackParam()
dataTrack.trackType = .data
config.publishTracks = [audioTrack, dataTrack]
config.subscribeTracks = [audioTrack, dataTrack]
engine.connect(config)
JSONObject obj = new JSONObject(responseText);
AoqClientEngine.AoqConnectConfig cfg = new AoqClientEngine.AoqConnectConfig();
cfg.token = obj.optString("aoqTokenForClient", "");
cfg.sid = obj.optString("sid", "");
cfg.certFingerprint = obj.optString("clientRelayCertFingerprint", "");
JSONArray arr = obj.optJSONArray("clientRelayEndpoints");
if (arr != null) {
for (int i = 0; i < arr.length(); i++) {
JSONObject o = arr.optJSONObject(i);
AoqClientEngine.AoqRelayEndpoint ep = new AoqClientEngine.AoqRelayEndpoint();
// route_index 缺省时按数组下标兜底
ep.routeIndex = o.has("route_index") ? o.optInt("route_index", i) : i;
ep.endpoint = o.optString("endpoint", "");
ep.port = o.optInt("port", 0);
cfg.relayEndpoints.add(ep);
}
}
JSONObject ext = obj.optJSONObject("extraInfo");
cfg.workspaceIdHash = ext != null ? ext.optString("workspaceIdHash", "") : "";
AoqClientEngine.AoqTrackParam audio = new AoqClientEngine.AoqTrackParam();
audio.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
AoqClientEngine.AoqTrackParam data = new AoqClientEngine.AoqTrackParam();
data.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
cfg.publishTracks.add(audio);
cfg.publishTracks.add(data);
cfg.subscribeTracks.add(audio);
cfg.subscribeTracks.add(data);
engine.connect(cfg);
const obj = JSON.parse(responseText) as Record<string, Object | undefined>;
const cfg: AoqConnectConfig = {
token: String(obj['aoqTokenForClient'] ?? ''),
sid: String(obj['sid'] ?? ''),
certFingerprint: String(obj['clientRelayCertFingerprint'] ?? ''),
// route_index 缺省时按数组下标兜底
relayEndpoints: (obj['clientRelayEndpoints'] as Array<any>).map((item, index) => ({
routeIndex: Number(item['route_index'] ?? index),
endpoint: String(item['endpoint'] ?? ''),
port: Number(item['port'] ?? 0)
})),
workspaceIdHash: String((obj['extraInfo'] as any)?.['workspaceIdHash'] ?? ''),
publishTracks: [
{ trackType: AoqTrackType.AoqTrackTypeAudio },
{ trackType: AoqTrackType.AoqTrackTypeData }
],
subscribeTracks: [
{ trackType: AoqTrackType.AoqTrackTypeAudio },
{ trackType: AoqTrackType.AoqTrackTypeData }
]
};
engine.connect(cfg);
整体流程
- 客户端通过业务 AppServer 获取连接凭证。
- 创建 SDK 引擎并注册回调。
- 配置媒体和 Data 轨道,关闭上行媒体发送。
- 建立 AOQ 连接,等待连接成功。
- 按模型协议完成配置,收到确认后开启所需媒体发送。
- 完成交互后断开连接并释放资源。
说明
-
音视频媒体数据由 AOQ SDK 接管,负责采集、处理、发送、接收和播放;Data 数据需要调用方遵循目标模型的事件定义进行发送和接收。
-
连接成功表示与模型的传输通道已建立,客户端应在模型初始化完成后再开启所需的上行媒体。对于本页示例型号
qwen3.8-omni-flash-realtime,发送session.update后需等待session.updated,再开启媒体发送。
1. 获取连接凭证
客户端向业务 AppServer 请求凭证。AppServer 使用本模型的 Realtime 建连地址,并携带 API Key 和 x-dashscope-rtc-transport: moq 请求网关。完整地址、请求和字段说明见 Token 鉴权。
将响应映射到 AoqConnectConfig:
| 网关响应 | SDK 配置 |
|---|---|
aoqTokenForClient | token |
sid | sid |
clientRelayCertFingerprint | certFingerprint |
clientRelayEndpoints | relayEndpoints,按结构转换各接入点 |
extraInfo 中的 workspaceIdHash | workspaceIdHash |
新建连接时获取新的连接凭证。同一连接内能否复用会话或启动下一轮任务,由目标模型协议决定。
2. 初始化 SDK 并注册回调
创建引擎并注册连接状态、Data 消息和错误回调。以 iOS Swift 为例:
let createConfig = AoqCreateConfig()
createConfig.workDir = workDir
createConfig.enableDumpAudio = false
engine = AoqClientEngine.createEngine(createConfig, delegate: self)
实现 AoqEngineDelegate,在 onConnectionStatusChange 中维护连接状态,在 onDataMsg 中解析目标模型事件,在 onError 中处理 SDK 错误。模型返回的错误事件也需要在 Data 消息处理逻辑中单独处理。SDK 接口及错误说明请参见 各平台 SDK和功能参考。
3. 配置 SDK
选择四个媒体方向
以下“推”和“拉”均以客户端为视角。按业务实际使用的输入、输出模态配置,不需要的方向不添加媒体轨道。
| 方向 | 配置内容 | 典型用途 |
|---|---|---|
| 推音频 | 发布 Audio 轨,设置音频编码器,配置内置或外部采集 | 语音对话 |
| 拉音频 | 订阅 Audio 轨,设置音频解码器,配置播放器或外部播放 | 语音回复 |
| 推视频 | 发布 Video 轨,配置摄像头或外部视频输入及编码参数 | 向支持视觉输入的模型发送画面 |
| 拉视频 | 本模型不支持视频输出 | 不配置视频订阅 |
Data 轨独立于这四个方向:发布 Data 轨用于发送模型事件,订阅 Data 轨用于接收回复。纯文本输入或输出也需要 Data 轨。
本模型的媒体方向
下表列出本模型的媒体方向。模型范围见适用范围,其他接入方式参见Qwen-Omni-Realtime。
| 模型/应用 | 推音频 | 拉音频 | 推视频 | 拉视频 |
|---|---|---|---|---|
| Qwen-Omni-Realtime | 语音输入时开启 | 需要语音回复时开启 | 需要视觉输入时开启 | 不支持开启 |
配置媒体参数和轨道
音频上行和下行应分别配置:上行使用 setAudioEncoderConfig,下行使用 setAudioDecoderConfig。这些参数用于网络传输,网关会将音频转换为模型所需的格式。传输编码配置与模型事件中的音频格式参数应分别按 SDK 和目标模型的要求设置。
按方向启动 startAudioCapture、startAudioPlayer 或 startVideoCapture。自定义输入输出见下文高阶能力。
publishTracks 表示客户端发送的轨道,subscribeTracks 表示客户端接收的轨道:
| 场景 | publishTracks | subscribeTracks |
|---|---|---|
| 语音对话 | Audio、Data | Audio、Data |
| 带画面输入的语音对话 | Audio、Video、Data | Audio、Data |
在建连前关闭上行媒体发送,避免模型尚未就绪时收到输入:
engine.enableSendMediaStream(.audio, enable: false)
engine.enableSendMediaStream(.video, enable: false)
采集、发布轨道和允许发送是不同的操作。关闭发送不等于停止本地采集,也不影响 Data 轨事件收发。未添加的轨道不会因开启发送而自动创建。
4. 建立连接
将凭证与轨道配置填入 AoqConnectConfig,调用:
engine.connect(connectConfig)
等待 onConnectionStatusChange 返回 .connected,再进入模型初始化流程。connect 调用返回不等于连接成功。
5. 按模型协议交互并收发媒体
发送和解析模型事件
客户端通过 Data 轨发送事件。以下为 iOS Swift 代码片段,其他接口详见 iOS SDK 参考:
// eventJSON 由目标模型的客户端事件协议构造。
let msg = AoqDataMsg()
msg.data = eventJSON.data(using: .utf8)!
engine.send(msg)
在 onDataMsg 中解析回复。本模型通过 type 字段标识事件类型。完整字段、参数及结束条件应遵循目标模型文档。
就绪后开启所需媒体
满足当前模型、当前会话或任务的就绪条件后,仅开启已配置的上行方向。例如,需要语音输入时调用:
engine.enableSendMediaStream(.audio, enable: true)
需要视频输入时再开启 .video。
音视频通过对应媒体轨道传输;模型事件和文本通过 Data 轨传输。例如,Omni 的 AOQ 接入无需使用 input_audio_buffer.append 或 input_image_buffer.append 重复发送媒体。下行音频由已配置的播放器或外部播放逻辑处理。
6. 结束交互并释放资源
不再使用连接时,由客户端业务层调用AOQ SDK 的断开连接和释放资源接口:
engine.disconnect()
AoqClientEngine.destroy()
异常断开后清理业务层就绪状态,按连接状态管理处理后续连接;重新建连后按模型协议重新初始化。
Data 通道发送和接收事件
客户端通过 AOQ SDK 的 Data 轨发送和接收模型事件。连接时需要发布和订阅 Data 轨;发送时将模型事件序列化后放入 AoqDataMsg,并调用所用平台的 Data 发送接口;接收时在 onDataMsg 回调中解析消息。接口详见各平台 SDK。
发送事件的名称、字段、参数和调用时机遵循目标模型的客户端事件定义;收到消息后的解析方式、状态变更、结果和错误处理遵循该模型的服务端事件定义。初始化事件根据模型要求按需发送,后续文本和控制事件按照业务需要发送。
| 模型/应用 | 客户端事件定义(发送) | 服务端事件定义(接收) |
|---|---|---|
| Qwen-Omni-Realtime | 客户端事件 | 服务端事件 |
模型事件的发送与接收还应遵循以下规则:
-
使用事件的
type字段识别模型事件,关联标识按本模型事件参考处理。 -
模型要求的初始化确认、输入提交、响应取消和任务结束等操作,均在 Data 通道中按对应协议处理;它们属于模型交互语义,与断开传输连接是不同操作。
-
结束前按本模型事件说明完成输入提交和结果接收;需要完整播放时,处理完本地待播音频再释放连接。
-
音视频数据通过媒体轨道传输。引用模型事件文档时,媒体封装和可用交互模式仍遵循本文的接入方式,不直接套用 WebSocket 的媒体发送方式。
高阶能力
| 能力 | 使用场景 | 文档 |
|---|---|---|
| 独立控制音视频发送 | 等待模型就绪、暂停上行、按需开启视频 | 媒体流发送管理 |
| 音频设备与处理 | 编解码、扬声器、文件混音、音频帧回调 | 音频常用功能 |
| 外部音频输入 | 使用业务侧音频源 | 自定义音频采集 |
| 外部音频输出 | 由业务侧处理或播放输出音频 | 自定义音频播放 |
| 视频采集与输入 | 摄像头及业务侧画面输入 | 视频常用功能、自定义视频输入 |
媒体发送控制只控制媒体传输。模型响应取消、轮次提交等行为仍需使用目标模型的事件协议。
会话配置示例
说明以下示例适用于 qwen3.8-omni-flash-realtime。音视频通过媒体轨道传输,不使用 WebSocket 的媒体发送事件。
{
"type": "session.update",
"session": {
"modalities": [
"text",
"audio"
],
"voice": "Tina",
"turn_detection": {
"type": "server_vad"
}
}
}
AOQ SDK 下载
以下版本更新说明描述 AOQ SDK 的通用能力,本模型的媒体支持范围见上方本模型的媒体方向。
| 版本 | 平台 | 下载 | 更新日期 | 更新说明 |
|---|---|---|---|---|
v1.4.0 | Android | 2026-10-27 |
| |
iOS | ||||
HarmonyOS | ||||
Windows | ||||
macOS | ||||
Electron | aoq-electron-sdk-1.4.0-59463182.tgz 也可以通过 npm 安装:
| |||
Linux |
| |||
| v1.3.0 | Android | 2026-09-18 |
| |
| iOS | ||||
| HarmonyOS | ||||
| Windows | AoqClientSdk-windows-x64.zip | |||
| macOS | ||||
| Electron | 也可以通过 npm 安装: npm install aoq-electron-sdk@1.3.0-58586345 | |||
| Linux | x86_64: AoqClientSdk.zip arm64: AoqClientSdk.zip | |||
| v1.2.2 | iOS | AoqClientSdk.framework.zip | 2026-09-09 |
|
| v1.2.0 | Android | 2026-08-17 |
| |
| iOS | ||||
| HarmonyOS | ||||
| Windows | ||||
| macOS | ||||
| Electron | 也可以通过npm安装: npm install aoq-electron-sdk@1.2.0-57407598 | |||
| Linux | x86_64:AoqClientSdk.zip arm64:AoqClientSdk.zip | |||
| v1.1.0 | Android | 2026-07-31 |
| |
| iOS | ||||
| HarmonyOS | ||||
| Linux | AoqClientSdk.zip | |||
| v1.0.1 | Android | 2026-07-09 |
| |
| iOS | ||||
| HarmonyOS |
重要AOQ使用音频插件的方式加载Opus编解码器,如果需要Opus编解码时需要下载Opus插件并加载到工程内。