Menggunakan AOQ dengan qwen3.5-omni-plus-realtime untuk panggilan real-time
Menjelaskan cara mengintegrasikan AOQ Client SDK pada Android, iOS, dan HarmonyOS untuk mengimplementasikan panggilan audio/video menggunakan AOQ dan qwen3.5-omni-plus-realtime.
Untuk AOQ Client SDK dan plug-in audio Opus, lihat SDK download. Kodek Opus disediakan sebagai plug-in terpisah. Sertakan sesuai kebutuhan kasus penggunaan Anda.
Letakkan AoqClientSdk-release.aar di direktori app/libs/ proyek Anda. Letakkan libPluginOpus.so berdasarkan ABI ke dalam app/libs/armeabi-v7a/ dan app/libs/arm64-v8a/. Di app/build.gradle:
RECORD_AUDIO dan CAMERA adalah izin runtime. Aplikasi Anda harus memanggil ActivityCompat.requestPermissions() milik Android saat runtime untuk meminta otorisasi pengguna.
Tarik AoqClientSdk.framework dan PluginOpus.framework ke dalam proyek Xcode Anda. Di bawah Target > General > Frameworks, Libraries, and Embedded Content, pilih Embed & Sign.
Deklarasikan izin: di Xcode, pilih Target Anda > Info > Custom iOS Target Properties, lalu tambahkan dua deskripsi penggunaan izin berikut:
Letakkan aoq-client-sdk.har di direktori libs/ proyek Anda. Letakkan libPluginOpus.so berdasarkan ABI ke dalam entry/libs/armeabi-v7a/ dan entry/libs/arm64-v8a/. Deklarasikan dependensi di entry/oh-package.json5.
Gunakan demo Android dari Alibaba Cloud Model Studio untuk memverifikasi konektivitas AOQ secara cepat. Unduh APK dan konfigurasikan Kunci API serta workspaceId untuk mencoba model yang dipilih.Pindai kode QR berikut untuk mengunduh dan menginstal demo Android:
Ikuti bagian AOQ dalam Token authentication untuk menyiapkan AppServer yang mendapatkan token. Sebelum setiap panggilan, klien harus meminta token dari AppServer Anda.
Panggil startAudioCapture dan startAudioPlayer untuk memulai tangkapan dan pemutaran audio lokal. Panggil startVideoCapture untuk mengaktifkan kamera, dan gunakan setLocalView untuk mengikat target render SDK ke kontrol pratinjau Anda.iOS:
Copy
// Audio capturelet capCfg = AoqAudioCaptureConfig()capCfg.channel = 1; capCfg.isExternal = falseengine.startAudioCapture(capCfg)// Audio playbacklet playCfg = AoqAudioPlaybackConfig()playCfg.channel = 1; playCfg.isExternal = falseengine.startAudioPlayer(playCfg)// Video capturelet vidCfg = AoqVideoCaptureConfig()vidCfg.width = 720; vidCfg.height = 1280; vidCfg.fps = 15engine.startVideoCapture(vidCfg)// Set render view for local video previewlet canvas = AoqVideoCanvas()canvas.view = localPreviewcanvas.renderMode = .cropengine.setLocalView(.video, canvas: canvas)
Android:
Copy
// Audio captureAoqAudioCaptureConfig capCfg = new AoqAudioCaptureConfig();capCfg.channel = 1; capCfg.isExternal = false;engine.startAudioCapture(capCfg);// Audio playbackAoqAudioPlaybackConfig playCfg = new AoqAudioPlaybackConfig();playCfg.channel = 1; playCfg.isExternal = false;engine.startAudioPlayer(playCfg);// Video captureAoqVideoCaptureConfig vidCfg = new AoqVideoCaptureConfig();vidCfg.width = 720; vidCfg.height = 1280; vidCfg.fps = 15;engine.startVideoCapture(vidCfg);// Set render view for local video previewAoqVideoCanvas canvas = new AoqVideoCanvas();canvas.view = localPreview;canvas.renderMode = AoqRenderMode.AoqRenderModeCrop;engine.setLocalView(AoqTrackType.AoqTrackTypeVideo, canvas);
Atur parameter kodek dan panggil connect.Catatan: qwen3.5-omni-plus-realtime mengharuskan klien mulai mengirim data media hanya setelah menerima session.updated dari server. Untuk menghindari pengiriman media secara tidak sengaja selama jendela antara connect yang berhasil dan kedatangan session.updated, panggil enableSendMediaStream(trackType, false) untuk setiap track upstream sebelum memanggil connect. Untuk detail event WebSocket, lihat Client events.iOS:
Important: AOQ SDK secara default mulai mengirim data media setelah terhubung. Contoh ini menunjukkan cara menonaktifkan pengiriman media saat terhubung ke model.
Dalam callback onConnectionStatusChange(Connected), kirim pesan session.update melalui sendDataMsg. Pesan ini berisi parameter sesi seperti modalities, voice, instructions, dan turn_detection, yang melengkapi handshake sesi. Untuk detail event WebSocket, lihat Client events.iOS:
Copy
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": "Ethan", // 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 for qwen3.5-omni-realtime. "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:
Copy
@Overridepublic 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:
Copy
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);}
Enable media sending after receiving session.updated
Dalam callback onDataMsg, uraikan pesan masuk. Saat menerima session.updated dari model, panggil enableSendMediaStream(trackType, true) untuk setiap jenis track yang sebelumnya dinonaktifkan. Untuk detail event WebSocket, lihat Server events.iOS:
Copy
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) }}
SDK terintegrasi secara mendalam dengan Alibaba Cloud Model Studio. Pesan barge-in dari model akan menginterupsi giliran sebelumnya ketika giliran baru dimulai.
SDK menyediakan antarmuka interruptAudioPlayer untuk interupsi pemutaran lokal. Panggil API ini ketika pengguna ingin menghentikan pemutaran.
// Pass the desired camera direction enumengine.switchCamera(AoqCameraDirection.AoqCameraDirectionFront);engine.switchCamera(AoqCameraDirection.AoqCameraDirectionBack);
Server mendorong hasil ASR dan respons teks AI melalui pesan data downstream. Di aplikasi Anda, arahkan berdasarkan bidang type dalam callback onDataMsg. Untuk detail event WebSocket, lihat Server events.
Singleton semantics: createEngine bersifat singleton — pemanggilan berulang mengembalikan instans yang sama. Engine hanya dapat dibuat ulang setelah destroy dipanggil. Untuk penggunaan multi-halaman, kelola siklus hidup engine di tingkat Application atau Ability.
Local preview view types:
Android: SurfaceView atau TextureView. Jenis lain tidak didukung.
iOS: Subkelas UIView apa pun.
HarmonyOS: Lihat dokumentasi SDK.
Audio route changes: Event seperti pemasangan/pencabutan headphone atau koneksi Bluetooth memicu onAudioDeviceRouteChanged. Biasanya tidak diperlukan penanganan di aplikasi Anda. Jika UI Anda menyertakan toggle speaker/earpiece, sinkronkan status berdasarkan callback ini.
Background audio continuation: Agar audio tetap berlanjut saat panggilan berpindah ke latar belakang, Info.plist harus memiliki UIBackgroundModes = audio diaktifkan dan AVAudioSession harus diaktifkan dengan benar di latar depan. SDK menangani sebagian besar kasus; gunakan setAudioSessionRestriction: untuk kontrol detail halus.