Chamada em tempo real com qwen3.8-omni-flash-realtime via AOQ
Use qwen3.8-omni-flash-realtime Modelo através do protocolo AOQ para chamadas em tempo real
Descreve como integrar o Cliente de AOQ SDK no Android, iOS e HarmonyOS para implementar chamadas de áudio/vídeo usando AOQ e qwen3.8-omni-flash-realtime.
Pegue o SDK.
Para o Cliente de AOQ SDK e o plug-in de áudio Opus, veja [SDK download] (/api-reference/realtime-api/sdk-download). Este exemplo usa Opus e requer o plug-in Opus.
Importar o SDK
Copie os arquivos SDK do núcleo para o diretório de dependência do seu projeto e declare as permissões necessárias em sua configuração do projeto.
Androide.
Lugar AoqClientSdk-release.aar em seu projeto. app/libs/ Diretório. Lugar libPluginOpus.so pela ABI em app/libs/armeabi-v7a/ e app/libs/arm64-v8a/. Em app/build.gradle:
android {
defaultConfig {
minSdk 21
ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' }
}
sourceSets { main { jniLibs.srcDirs = ['libs'] } }
packagingOptions {
// Avoid conflicts with same-named .so files in the host project
pickFirsts += ['lib/*/*.so']
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar'])
}
Declare permissão. AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.CAMERA" />
RECORD_AUDIO e CAMERA são permissões de execução. Seu aplicativo deve chamar o Android's. ActivityCompat.requestPermissions() No horário de execução para solicitar autorização do usuário.
iOS (quadro)
-
Arrastar
AoqClientSdk.frameworkePluginOpus.frameworkem seu projeto Xcode. Sob o alvo > Geral > Frameworks, Bibliotecas, e Conteúdo Incorporado, selecione Embed & Sign. -
Declare permissões: no Xcode, selecione o seu Alvo > Informações > Propriedades de Destino Personalizadas do iOS e adicione as seguintes duas descrições de uso de permissão:
| Chave | Valor |
|---|---|
NSMicrophoneUsageDescription |
Para chamadas de voz em tempo real |
NSCameraUsageDescription |
Para chamadas de vídeo em tempo real. |
- Projeto rápido:
import AoqClientSdk. Projeto objetivo C:#import <AoqClientSdk/AoqClientSdk.h>.
HarmonyOS (har)
-
Lugar
aoq-client-sdk.harem seu projeto.libs/Diretório. LugarlibPluginOpus.sopela ABI ementry/libs/armeabi-v7a/eentry/libs/arm64-v8a/. Declare a dependência ementry/oh-package.json5. -
Adicione permissões.
entry/src/main/module.json5:
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.MICROPHONE",
"reason": "$string:perm_mic_reason",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } },
{ "name": "ohos.permission.CAMERA",
"reason": "$string:perm_camera_reason",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } }
]
- Em
EntryAbility, gatilho de autorização de execução viaabilityAccessCtrl.createAtManager().requestPermissionsFromUser.
Pegue um símbolo do AppServer.
Siga a seção do AOQ em [Autenticação do Token] (/api-reference/realtime-api/token-auth) para criar um AppServer que obtenha fichas. Antes de cada chamada, o cliente deve pedir um sinal do seu AppServer.
Implementar chamadas de áudio e vídeo de IA
Crie motores e ajuste chamadas.
Chame o createEngine API para criar um AoqClientEngine - Por exemplo.
iOS
```swift
let config = AoqCreateConfig()
config.workDir = workDir
engine = AoqClientEngine.createEngine(config, delegate: self)
```
Implementar o `AoqEngineDelegate` Protocolo para ouvir `onConnectionStatusChange`, `onDataMsg`, `onError`E outras chamadas.
Android
```java
AoqCreateConfig config = new AoqCreateConfig();
config.workDir = appCtx.getFilesDir().getAbsolutePath();
engine = AoqClientEngine.createEngine(appCtx, config, this);
```
HarmonyOS
```typescript
const config: AoqCreateConfig = { workDir: context.filesDir, extras: '' };
engine = AoqClientEngine.createEngine(config, this, context);
```
Iniciar captura de áudio/vídeo e reprodução
Chamada. startAudioCapture e startAudioPlayer para iniciar a captura de áudio local e reprodução. Chamada. startVideoCapture para iniciar a câmera, e usar setLocalView Ligar o alvo de renderização do SDK ao seu controle de visualização.
iOS
```swift
// Audio capture
let capCfg = AoqAudioCaptureConfig()
capCfg.channel = 1; capCfg.isExternal = false
engine.startAudioCapture(capCfg)
// Audio playback
let playCfg = AoqAudioPlaybackConfig()
playCfg.channel = 1; playCfg.isExternal = false
engine.startAudioPlayer(playCfg)
// Video capture
let vidCfg = AoqVideoCaptureConfig()
vidCfg.width = 720; vidCfg.height = 1280; vidCfg.fps = 15
engine.startVideoCapture(vidCfg)
// Set render view for local video preview
let canvas = AoqVideoCanvas()
canvas.view = localPreview
canvas.renderMode = .crop
engine.setLocalView(.video, canvas: canvas)
```
Android
```java
// Audio capture
AoqAudioCaptureConfig capCfg = new AoqAudioCaptureConfig();
capCfg.channel = 1; capCfg.isExternal = false;
engine.startAudioCapture(capCfg);
// Audio playback
AoqAudioPlaybackConfig playCfg = new AoqAudioPlaybackConfig();
playCfg.channel = 1; playCfg.isExternal = false;
engine.startAudioPlayer(playCfg);
// Video capture
AoqVideoCaptureConfig vidCfg = new AoqVideoCaptureConfig();
vidCfg.width = 720; vidCfg.height = 1280; vidCfg.fps = 15;
engine.startVideoCapture(vidCfg);
// Set render view for local video preview
AoqVideoCanvas canvas = new AoqVideoCanvas();
canvas.view = localPreview;
canvas.renderMode = AoqRenderMode.AoqRenderModeCrop;
engine.setLocalView(AoqTrackType.AoqTrackTypeVideo, canvas);
```
HarmonyOS
```typescript
// Audio capture
const capCfg: AoqAudioCaptureConfig = { channel: 1, isExternal: false };
engine.startAudioCapture(capCfg);
// Audio playback
const playCfg: AoqAudioPlaybackConfig = { channel: 1, isExternal: false };
engine.startAudioPlayer(playCfg);
// Video capture
const vidCfg: AoqVideoCaptureConfig = { width: 720, height: 1280, fps: 15, isExternal: false };
engine.startVideoCapture(vidCfg);
// Set render view for local video preview
const canvas: AoqVideoCanvas = { view: localCtrl, renderMode: AoqRenderMode.AoqRenderModeCrop };
engine.setLocalView(AoqTrackType.AoqTrackTypeVideo, canvas);
```
Obter credenciais de conexão
Peça ao seu servidor que faça o pedido. QwenCloud. Veja [Autenticação do Token] (/api-reference/realtime-api/token-auth).
Configure os codecs e estabeleça conexão.
Defina parâmetros de codec e chame connect.
Nota: qwen3.8-omni-flash-realtime requer que o cliente comece a enviar dados de mídia apenas após receber session.updated do servidor. Para evitar acidentalmente enviar mídia durante a janela entre um sucesso connect e a chegada de session.updated, chamar enableSendMediaStream(trackType, false) para cada faixa de upstream antes de chamar connect. Para detalhes do evento WebSocket, veja [Eventos do cliente] (/api-reference/real-time-multimodal/client-events).
iOS
```swift
// Audio codec configuration
let encCfg = AoqAudioCodecConfig()
encCfg.codecType = .audioOpus; encCfg.sampleRate = 16000; encCfg.channel = 1
engine.setAudioEncoderConfig(encCfg)
engine.setAudioDecoderConfig(encCfg)
// Disable media sending before connect; enable after receiving session.updated
engine.enableSendMediaStream(.audio, enable: false)
engine.enableSendMediaStream(.video, enable: false)
// Establish connection
let conn = AoqConnectConfig()
conn.token = token; conn.sid = sid; conn.certFingerprint = cert
conn.relayEndpoints = endpoints; conn.workspaceIdHash = workspaceIdHash
let aTrack = AoqTrackParam(); aTrack.trackType = .audio
let vTrack = AoqTrackParam(); vTrack.trackType = .video
let dTrack = AoqTrackParam(); dTrack.trackType = .data
conn.publishTracks = [aTrack, vTrack, dTrack]
conn.subscribeTracks = [aTrack, dTrack]
engine.connect(conn)
```
Android
```java
// Audio codec configuration
AoqAudioCodecConfig encCfg = new AoqAudioCodecConfig();
encCfg.codecType = AoqEncoderType.AoqEncoderTypeAudioOpus;
encCfg.sampleRate = 16000; encCfg.channel = 1;
engine.setAudioEncoderConfig(encCfg);
engine.setAudioDecoderConfig(encCfg);
// Disable media sending before connect; enable after receiving session.updated
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, false);
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeVideo, false);
// Establish connection
AoqConnectConfig conn = new AoqConnectConfig();
conn.token = token; conn.sid = sid; conn.certFingerprint = cert;
conn.relayEndpoints.addAll(endpoints); conn.workspaceIdHash = workspaceIdHash;
AoqTrackParam aTrack = new AoqTrackParam(); aTrack.trackType = AoqTrackType.AoqTrackTypeAudio;
AoqTrackParam vTrack = new AoqTrackParam(); vTrack.trackType = AoqTrackType.AoqTrackTypeVideo;
AoqTrackParam dTrack = new AoqTrackParam(); dTrack.trackType = AoqTrackType.AoqTrackTypeData;
conn.publishTracks.add(aTrack);
conn.publishTracks.add(vTrack);
conn.publishTracks.add(dTrack);
conn.subscribeTracks.add(aTrack);
conn.subscribeTracks.add(dTrack);
engine.connect(conn);
```
HarmonyOS
```typescript
// Audio codec configuration
const encCfg: AoqAudioCodecConfig = {
codecType: AoqEncoderType.AoqEncoderTypeAudioOpus,
sampleRate: 16000, channel: 1
};
engine.setAudioEncoderConfig(encCfg);
engine.setAudioDecoderConfig(encCfg);
// Disable media sending before connect; enable after receiving session.updated
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, false);
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeVideo, false);
// Establish connection
const conn: AoqConnectConfig = {
token, sid, certFingerprint: cert,
relayEndpoints: endpoints,
workspaceIdHash,
publishTracks: [
{ trackType: AoqTrackType.AoqTrackTypeAudio },
{ trackType: AoqTrackType.AoqTrackTypeVideo },
{ trackType: AoqTrackType.AoqTrackTypeData }
],
subscribeTracks: [
{ trackType: AoqTrackType.AoqTrackTypeAudio },
{ trackType: AoqTrackType.AoqTrackTypeData }
]
};
engine.connect(conn);
```
O SDK do AOQ começa a enviar dados de mídia por padrão após a conexão. Este exemplo demonstra como desativar o envio de mídia ao conectar-se a um modelo.
Configure a sessão de IA
Na onConnectionStatusChange(Connected) -Retorne, envie um. session.update mensagem via sendDataMsg. Esta mensagem contém parâmetros de sessão, como modalidades, voz, instruções, e detecção, completando o aperto de mão da sessão. Para detalhes do evento WebSocket, veja [Eventos do cliente] (/api-reference/real-time-multimodal/client-events).
iOS
```swift
func onConnectionStatusChange(_ status: AoqConnectionStatus) {
if status == .connected { sendSessionUpdate() }
}
private func sendSessionUpdate() {
let json = """
{
// Event ID, generated by the client
"event_id": "event_ToPZqeobitzUJnt3QqtWg",
// Event type, always session.update
"type": "session.update",
// Session configuration
"session": {
// Output modalities: ["text"] (text only) or ["text","audio"] (text and audio)
"modalities": [
"text",
"audio"
],
// Output audio voice
"voice": "Tina",
// Input audio format; currently only "pcm" is supported. Input audio is 16 kHz PCM.
"input_audio_format": "pcm",
// Output audio format; currently only "pcm" is supported. Output audio is 24 kHz PCM.
"output_audio_format": "pcm",
// System message for setting the model's objective or role.
"instructions": "You are an AI customer service agent for a five-star hotel. Accurately and helpfully answer customer inquiries about room types, facilities, pricing, and reservation policies. Always respond professionally and helpfully. Do not provide unverified information or information outside the scope of hotel services.",
// Whether to enable voice activity detection. If enabled, pass a config object and the server will automatically detect voice start and end.
// Set to null to let the client control when to trigger a model response.
"turn_detection": {
// VAD type: server_vad or semantic_vad. Recommended: semantic_vad.
"type": "semantic_vad",
// VAD detection threshold. Increase in noisy environments; decrease in quiet ones.
"threshold": 0.5,
// Duration of silence after which a model response is triggered
"silence_duration_ms": 800
}
}
}
"""
let msg = AoqDataMsg()
msg.data = json.data(using: .utf8)!
engine.send(msg)
}
```
Android
```java
@Override
public void onConnectionStatusChange(AoqConnectionStatus status) {
if (status == AoqConnectionStatus.AoqConnectionStatusConnected) {
sendSessionUpdate();
}
}
private void sendSessionUpdate() {
String sessionUpdateJson = /* Same session.update JSON as in the Swift example above */;
AoqDataMsg msg = new AoqDataMsg();
msg.data = sessionUpdateJson.getBytes(StandardCharsets.UTF_8);
engine.sendDataMsg(msg);
}
```
HarmonyOS
```typescript
onConnectionStatusChange(status: AoqConnectionStatus): void {
if (status === AoqConnectionStatus.AoqConnectionStatusConnected) {
this.sendSessionUpdate();
}
}
private sendSessionUpdate(): void {
const sessionUpdateJson = /* Same session.update JSON as in the Swift example above */;
const msg: AoqDataMsg = { data: new TextEncoder().encode(sessionUpdateJson).buffer };
this.engine.sendDataMsg(msg);
}
```
Habilitar envio de mídia após receber sessão.
Na onDataMsg Chamada de volta, analisar mensagens recebidas. Quando session.updated é recebido do modelo, ligue enableSendMediaStream(trackType, true) para cada tipo de pista que foi desativado antes. Para detalhes do evento WebSocket, veja [Eventos de servidor] (/api-reference/real-time-multimodal/server-events).
iOS
```swift
func onDataMsg(_ msg: AoqDataMsg) {
guard let obj = try? JSONSerialization.jsonObject(with: msg.data) as? [String: Any],
let type = obj["type"] as? String else { return }
if type == "session.updated" {
engine.enableSendMediaStream(.audio, enable: true)
engine.enableSendMediaStream(.video, enable: true)
}
}
```
Android
```java
@Override
public void onDataMsg(AoqDataMsg msg) {
if (msg == null || msg.data == null) return;
try {
JSONObject obj = new JSONObject(new String(msg.data, StandardCharsets.UTF_8));
if ("session.updated".equals(obj.optString("type"))) {
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, true);
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeVideo, true);
}
} catch (JSONException ignored) {}
}
```
HarmonyOS
```typescript
onDataMsg(msg: AoqDataMsg): void {
if (!msg?.data) return;
try {
const text = new TextDecoder('utf-8').decode(new Uint8Array(msg.data));
const obj = JSON.parse(text) as { type?: string };
if (obj.type === 'session.updated') {
this.engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, true);
this.engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeVideo, true);
}
} catch (_) { /* Not JSON, ignore */ }
}
```
- Comece a enviar transmissões de mídia só após receber
session.updated. A IA pode não estar pronta para receber dados antes deste evento. - As faixas de áudio e vídeo adicionadas durante a conexão (canais de mídia AOQ) automaticamente entregam dados ao servidor.
- Áudio: transmitido diretamente através da faixa de áudio - não há necessidade de enviar
input_audio_buffer.appendeventos. - Vídeo: quadros são enviados através da faixa de vídeo - não há necessidade de enviar
input_image_buffer.appendeventos.
- Áudio: transmitido diretamente através da faixa de áudio - não há necessidade de enviar
Desligue e destrua o motor.
engine.disconnect()
AoqClientEngine.destroy()
Cenários comuns
Barge-in
- O SDK está profundamente integrado com QwenCloud. Mensagens de entrada do modelo interrompem a curva anterior quando começa uma nova curva.
- O SDK fornece o
interruptAudioPlayerInterface para interrupção local da reprodução. Chame esta API quando o usuário wants Para parar a reprodução.
// iOS
engine.interruptAudioPlayer(.audio, fadeMs: 100)
Mudo / unmute
Após silenciar, o SDK continua a capturar áudio, mas envia apenas quadros silenciosos. O session Não é interrompido.
engine.muteAudioCapture(true); // Mute microphone (capture continues, but only silent frames are sent)
engine.muteAudioCapture(false); // Unmute
Troque entre as câmeras dianteira e traseira.
// Pass the desired camera direction enum
engine.switchCamera(AoqCameraDirection.AoqCameraDirectionFront);
engine.switchCamera(AoqCameraDirection.AoqCameraDirectionBack);
Legendas e resultados da ASR
O servidor empurra os resultados da ASR e as respostas de texto da IA através de mensagens de dados a jusante. Em seu aplicativo, roteie-os pelo type - Não, não, não. onDataMsg -Retorne. Para detalhes do evento WebSocket, veja [Eventos de servidor] (/api-reference/real-time-multimodal/server-events).
Notas importantes.
-
#Semântica de singleton#
createEngineÉ um singleton. Chamadas repetidas retornam o mesmo caso. O motor só pode ser recriado depois.destroyé chamado. Para uso em várias páginas, gerencie o ciclo de vida do motor no nível de Aplicação ou Capacidade. -
** Tipos de visualização local:
- Androide:
SurfaceViewOuTextureView. Outros tipos não são suportados. - Qualquer um.
UIView- Subclasse. - Veja a documentação da SDK.
- Androide:
-
Acontecimentos como inserção/remoção de fones de ouvido ou gatilho de conexão Bluetooth
onAudioDeviceRouteChanged. Não é necessário nenhum manuseio no seu aplicativo. Se a sua interface incluir um botão de voz/outorga, sincronize o estado com base nessa chamada. -
Para o áudio continuar quando a chamada se mover para o fundo,
Info.plistDeve ter.UIBackgroundModes = audiohabilitado eAVAudioSessiondeve ser corretamente ativado em primeiro plano. O SDK lida com a maioria dos casos; usesetAudioSessionRestriction:para controle de grãos finos.
Código fonte da demonstração iOS
[Interface demo dos iOS] (https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/en-US/images/p1087917.png)
Para aprender a implementar o AOQ no iOS, baixe o código fonte da amostra:https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/en-US/20260715/pnvhed/aoqdemo.zip).
Documentos relacionados
- Cliente de AOQ detalhado SDK API: [SDK visão geral] (/api-reference/realtime-api/aoq-sdk-intro)
- qwen3.8-omni-flash-realtime Eventos do cliente modelo:/api-reference/real-time-multimodal/client-events)
- qwen3.8-omni-flash-realtime eventos do servidor de modelos: [Eventos do servidor] (/api-reference/real-time-multimodal/server-events)