Tous les produits
Search
Centre de documentation

Alibaba Cloud Model Studio:Créer des conversations vocales push-to-talk avec qwen3.5-omni-plus-realtime via AOQ

Dernière mise à jour :Sep 07, 2026

Utilisez AOQ pour vous connecter à qwen3,5-omni-plus-realtime et permettre au client de contrôler les limites de tour pour les conversations en mode push-to-talk, avec la possibilité d'inclure des questions basées sur des images. Le code client est implémenté en Swift pour iOS.

Présentation de la solution

Qwen-Omni-Realtime prend en charge la détection automatique de voix (VAD) côté serveur ainsi que le mode manuel contrôlé par le client. Ce tutoriel définit session.turn_detection sur null. Le client transmet l'audio tant que l'utilisateur maintient un bouton enfoncé, valide l'audio et demande explicitement une réponse lorsque l'utilisateur relâche le bouton.

Le mode manuel convient aux boutons d'interphone matériels, aux commandes de type « appuyer et maintenir », aux environnements bruyants où l'application détermine les limites de tour, ainsi qu'aux tours incluant éventuellement une image. L'audio est transporté via la piste audio AOQ. N'envoyez pas input_audio_buffer.append.

Élément

Mode VAD

Mode manuel

Limite de tour

Détectée par server_vad ou semantic_vad

Contrôlée par un bouton ou l'état de l'application

Paramètre de session

turn_detection contient les paramètres VAD

turn_detection est null

Validation audio

Effectuée automatiquement par le service

Le client envoie input_audio_buffer.commit

Déclenchement de la réponse

Déclenché automatiquement par le service

Le client envoie response.create

Entrée d'image

Piste vidéo continue ou image via la piste de données

Piste vidéo continue ou image via la piste de données

Prérequis

  1. Activez Model Studio et suivez les instructions de la rubrique Obtenir et configurer une clé API. Stockez la clé API uniquement sur votre serveur d'application. Ne l'incluez pas dans le code client et ne la commitez pas dans un dépôt de code.
  2. Confirmez l'endpoint AOQ pour la région dans laquelle votre application est déployée. Pour obtenir des conseils de sélection, consultez la rubrique Sélectionner une région, une étendue de déploiement et un endpoint.
  3. Téléchargez la dernière version du SDK Client AOQ comme décrit dans la rubrique Téléchargement du SDK.
  4. Développez un serveur d'application et mettez en œuvre l'authentification proxy comme indiqué dans la rubrique Authentification par jeton. Avant chaque nouvelle connexion, le client doit obtenir de nouvelles informations d'identification auprès du serveur d'application.

Importer le SDK

Importez le SDK correspondant à votre plateforme de développement. L'implémentation cliente utilise Swift pour iOS. Les autres plateformes proposent les mêmes interfaces et le même flux d'événements. Ce tutoriel utilise des flux audio PCM. L'encodage Opus est fourni par un plugin. Importez le plugin Opus si la liaison montante utilise Opus.

Android

  1. Placez AoqClientSdk-release.aar dans app/libs, puis configurez la dépendance et les ABI prises en charge par le SDK dans app/build.gradle :

    android {
        defaultConfig {
            minSdk 21
            ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.aar'])
    }
    
  2. Déclarez les permissions suivantes dans 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" />
    
  3. Demandez les permissions RECORD_AUDIO et CAMERA au moment de l'exécution avant d'utiliser les périphériques correspondants.

iOS

  1. Ajoutez AoqClientSdk.framework au projet Xcode et sélectionnez Embed & Sign sous Target > General > Frameworks, Libraries, and Embedded Content. Le SDK prend en charge les appareils arm64 exécutant iOS 13.0 ou version ultérieure.
  2. Ajoutez NSMicrophoneUsageDescription et NSCameraUsageDescription à Info.plist et demandez l'autorisation avant d'utiliser les périphériques correspondants.
  3. Utilisez import AoqClientSdk en Swift ou #import <AoqClientSdk/AoqClientSdk.h> en Objective-C.

HarmonyOS

  1. Placez AoqClientSdk.har dans entry/libs et déclarez la dépendance dans entry/oh-package.json5. Le SDK est compatible avec l'API 12 et prend en charge arm64-v8a :

    {
      "dependencies": {
        "@aoq/client-sdk": "file:./libs/AoqClientSdk.har"
      }
    }
    
  2. Déclarez les permissions suivantes dans 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" } }
    ]
    
  3. Avant d'utiliser les périphériques correspondants, appelez abilityAccessCtrl.createAtManager().requestPermissionsFromUser pour demander ohos.permission.MICROPHONE et ohos.permission.CAMERA.

Linux (Python)

  1. Extrayez le SDK et conservez aoq_client_sdk.py, libAoqClientSdk.so et libonnxruntime.so.1.16.3 dans le même répertoire.

  2. Ajoutez le répertoire du SDK aux chemins de recherche Python et des bibliothèques dynamiques :

    export PYTHONPATH="$PWD/AoqClientSdk:$PYTHONPATH"
    export LD_LIBRARY_PATH="$PWD/AoqClientSdk:$LD_LIBRARY_PATH"
    
  3. Utilisez import aoq_client_sdk en Python. Vous pouvez également spécifier le chemin absolu de libAoqClientSdk.so en utilisant AOQ_CLIENT_SDK_LIB.

Flux d'implémentation

  1. Le serveur d'application obtient les paramètres de connexion AOQ pour qwen3,5-omni-plus-realtime à partir de l'URL du jeton Realtime.
  2. Le client crée le moteur et configure les codecs audio et les pistes. Il configure également la piste vidéo si une compréhension visuelle continue est requise.
  3. Le client démarre la capture et la lecture locales, désactive l'envoi sur la piste audio par défaut, se connecte à AOQ et envoie session.update.
  4. Une fois session.updated reçu, l'option continuous-video active la piste vidéo. La piste audio reste désactivée jusqu'à ce que l'utilisateur appuie sur le bouton de parole.
  5. Lorsque l'utilisateur appuie sur le bouton, le client active la piste audio. Au relâchement, il désactive la piste audio, envoie éventuellement une image, puis envoie input_audio_buffer.commit et response.create.
  6. Après réception de response.done, un nouveau tour peut commencer. Pour terminer, arrêtez les périphériques, déconnectez-vous et détruisez le moteur.

Piste vidéo continue

Publiez la piste vidéo et activez l'envoi vidéo après session.updated. Le modèle voit continuellement les dernières images. Chaque tour vocal nécessite uniquement la validation de l'audio et la demande de réponse.

Diagramme de séquence pour le mode manuel AOQ avec streaming vidéo continu

Envoyer une image via la piste de données

Ne publiez pas la piste vidéo. Lorsqu'une image est requise, envoyez input_image_buffer.append après le relâchement du bouton, puis validez l'audio et demandez une réponse.

Diagramme de séquence pour l'envoi d'images à la demande en mode manuel AOQ

Obtenir un jeton depuis le serveur d'application

Définissez la variable DASHSCOPE_API_KEY sur le serveur d'application et envoyez la requête à l'endpoint de la région sélectionnée. Le paramètre clientIp correspond à l'adresse IP publique réelle du client. Bien que ce champ soit facultatif, sa spécification aide le service à allouer un endpoint de relais approprié.

curl -X POST \
  "https://{endpoint}/api/v1/webrtc/realtime?model=qwen3.5-omni-plus-realtime" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${DASHSCOPE_API_KEY}" \
  -H "x-dashscope-rtc-transport: moq" \
  -d "{\"clientIp\": \"${CLIENT_REAL_IP}\"}"

RemarqueSi le serveur d'application ne peut pas obtenir l'adresse IP publique réelle du client, omettez le paramètre clientIp au lieu de transmettre une chaîne vide.

Le serveur d'application renvoie les champs de réponse suivants au client. Ne communiquez jamais la clé API à un client en production. Pour tous les champs de requête et de réponse, consultez Authentification par jeton.

Champ de réponse

Champ SDK

aoqTokenForClient

AoqConnectConfig.token

sid

AoqConnectConfig.sid

clientRelayCertFingerprint

AoqConnectConfig.certFingerprint

clientRelayEndpoints

AoqConnectConfig.relayEndpoints

extraInfo.workspaceIdHash

AoqConnectConfig.workspaceIdHash

Implémenter le client iOS

Une fois que le client a obtenu AoqConnectConfig depuis le serveur d'application, suivez ces étapes pour mettre en œuvre des conversations vocales push-to-talk sur iOS.

1. Créer le moteur et enregistrer les callbacks

Créez le singleton AOQ engine et enregistrez l'objet application en tant que destinataire des callbacks. Gérez les états de connexion, les événements serveur, les erreurs et les avertissements dans les callbacks.

let createConfig = AoqCreateConfig()
createConfig.workDir = workDir
engine = AoqClientEngine.createEngine(createConfig, delegate: self)

2. Démarrer les périphériques audio et vidéo

Initialisez la capture et la lecture audio. Démarrez la caméra uniquement pour l'option continuous-video. Obtenez les autorisations microphone et caméra avant d'appeler ces méthodes.

let captureConfig = AoqAudioCaptureConfig()
captureConfig.channel = 1
captureConfig.isExternal = false
engine.startAudioCapture(captureConfig)

let playbackConfig = AoqAudioPlaybackConfig()
playbackConfig.channel = 1
playbackConfig.isExternal = false
playbackConfig.isDefaultSpeaker = true
engine.startAudioPlayer(playbackConfig)

3. Configurer les codecs et les tracks

Configurez les codecs audio pour le modèle sélectionné et le format audio de l'application, puis sélectionnez les tracks pour l'option image-input. Les valeurs audio et vidéo suivantes sont données à titre d'exemple. Ajustez-les selon les exigences du modèle et le scénario d'application. Désactivez l'envoi Audio-track avant l'établissement de la connexion.

Track Continuous Video

Configurez les tracks publish Audio, Video et Data. Ajustez les paramètres d'encodage vidéo pour obtenir la qualité d'image requise et tenir compte de la bande passante disponible.

let audioEncoderConfig = AoqAudioCodecConfig()
audioEncoderConfig.trackType = .audio
audioEncoderConfig.codecType = .audioPCM
audioEncoderConfig.sampleRate = 16_000
audioEncoderConfig.channel = 1
engine.setAudioEncoderConfig(audioEncoderConfig)

let audioDecoderConfig = AoqAudioCodecConfig()
audioDecoderConfig.trackType = .audio
audioDecoderConfig.codecType = .audioPCM
audioDecoderConfig.sampleRate = 24_000
audioDecoderConfig.channel = 1
engine.setAudioDecoderConfig(audioDecoderConfig)

let videoEncoderConfig = AoqVideoCodecConfig()
videoEncoderConfig.trackType = .video
videoEncoderConfig.codecType = .videoJpeg
videoEncoderConfig.width = 960
videoEncoderConfig.height = 540
videoEncoderConfig.fps = 2
videoEncoderConfig.bitrate = 500_000
engine.setVideoEncoderConfig(videoEncoderConfig)

let publishAudioTrack = AoqTrackParam()
publishAudioTrack.trackType = .audio
let publishVideoTrack = AoqTrackParam()
publishVideoTrack.trackType = .video
let publishDataTrack = AoqTrackParam()
publishDataTrack.trackType = .data
let subscribeAudioTrack = AoqTrackParam()
subscribeAudioTrack.trackType = .audio
let subscribeDataTrack = AoqTrackParam()
subscribeDataTrack.trackType = .data
connectConfig.publishTracks = [publishAudioTrack, publishVideoTrack, publishDataTrack]
connectConfig.subscribeTracks = [subscribeAudioTrack, subscribeDataTrack]

Envoyer une image via la track Data

Configurez uniquement les tracks Audio et Data. Ne configurez pas d'encodeur vidéo, ce qui évite la capture, l'encodage et la transmission continus de la vidéo. Les valeurs audio sont données à titre d'exemple.

let audioEncoderConfig = AoqAudioCodecConfig()
audioEncoderConfig.trackType = .audio
audioEncoderConfig.codecType = .audioPCM
audioEncoderConfig.sampleRate = 16_000
audioEncoderConfig.channel = 1
engine.setAudioEncoderConfig(audioEncoderConfig)

let audioDecoderConfig = AoqAudioCodecConfig()
audioDecoderConfig.trackType = .audio
audioDecoderConfig.codecType = .audioPCM
audioDecoderConfig.sampleRate = 24_000
audioDecoderConfig.channel = 1
engine.setAudioDecoderConfig(audioDecoderConfig)

let publishAudioTrack = AoqTrackParam()
publishAudioTrack.trackType = .audio
let publishDataTrack = AoqTrackParam()
publishDataTrack.trackType = .data
let subscribeAudioTrack = AoqTrackParam()
subscribeAudioTrack.trackType = .audio
let subscribeDataTrack = AoqTrackParam()
subscribeDataTrack.trackType = .data
connectConfig.publishTracks = [publishAudioTrack, publishDataTrack]
connectConfig.subscribeTracks = [subscribeAudioTrack, subscribeDataTrack]

4. Configurer une session Manual

Une fois la connexion établie, appelez sendDataMsg pour envoyer un événement session.update. Définissez turn_detection sur null et sélectionnez la voix, les instructions et les modalités de sortie pour votre application. Maintenez la cohérence des paramètres audio de l'exemple avec les paramètres codec du SDK. Pour tous les champs, consultez Événements client.

private func sendSessionUpdate() {
    let event: [String: Any] = [
        "type": "session.update",
        "session": [
            "modalities": ["text", "audio"],
            "voice": "Ethan",
            "audio": [
                "input": ["format": ["type": "pcm", "sample_rate": 16_000]],
                "output": ["format": ["type": "pcm", "sample_rate": 24_000]]
            ],
            "turn_detection": NSNull()
        ]
    ]
    guard let data = try? JSONSerialization.data(withJSONObject: event) else { return }
    let dataMessage = AoqDataMsg()
    dataMessage.data = data
    engine.sendDataMsg(dataMessage)
}

5. Attendre la configuration de la session

Gérez l'événement session.updated dans le callback onDataMsg. N'envoyez aucun média tant que cet événement n'est pas reçu. Pour l'option continuous-video, appelez enableSendMediaStream pour activer la track Video à ce stade, mais maintenez la track Audio désactivée afin que l'audio précédant l'appui sur le bouton n'entre pas dans le tampon d'entrée.

func onDataMsg(_ msg: AoqDataMsg) {
    guard let event = try? JSONSerialization.jsonObject(with: msg.data) as? [String: Any],
          let type = event["type"] as? String else { return }
    if type == "session.updated", imageMode == .continuousVideo {
        engine.enableSendMediaStream(.video, enable: true)
    }
    // Keep Audio-track sending disabled until the talk button is pressed.
}

6. Implémenter l'interaction push-to-talk

Lorsque le bouton est enfoncé, appelez enableSendMediaStream pour activer la track Audio. Au relâchement, appelez enableSendMediaStream pour désactiver la track Audio, assurez-vous que le turn contient de l'audio, envoyez éventuellement une image, puis appelez sendDataMsg pour envoyer input_audio_buffer.commit suivi de response.create.

func onPushToTalkPressed() {
    hasAudioInCurrentTurn = true
    engine.enableSendMediaStream(.audio, enable: true)
}

func onPushToTalkReleased(base64Jpeg: String? = nil) {
    engine.enableSendMediaStream(.audio, enable: false)
    guard hasAudioInCurrentTurn else { return }
    if imageMode == .singleImage, let base64Jpeg {
        let imageEvent: [String: Any] = [
            "type": "input_image_buffer.append",
            "image": base64Jpeg
        ]
        if let data = try? JSONSerialization.data(withJSONObject: imageEvent) {
            let dataMessage = AoqDataMsg()
            dataMessage.data = data
            engine.sendDataMsg(dataMessage)
        }
    }
    for event in [
        ["type": "input_audio_buffer.commit"],
        ["type": "response.create"]
    ] {
        guard let data = try? JSONSerialization.data(withJSONObject: event) else { continue }
        let dataMessage = AoqDataMsg()
        dataMessage.data = data
        engine.sendDataMsg(dataMessage)
    }
    hasAudioInCurrentTurn = false
}

7. Sélectionnez une option d'entrée d'image

La compréhension visuelle continue et les questions ponctuelles sur des images utilisent des configurations de piste et des comportements d'envoi différents. Sélectionnez une option en fonction de la bande passante, de la consommation d'énergie et de la conception de l'interaction.

Continuous Video track

Utilisez cette option pour les appels vidéo, les scènes changeant rapidement ou le contexte visuel continu. Une fois la piste vidéo publiée, n'envoyez pas input_image_buffer.append.

Send a single image over the Data track

Utilisez cette option pour les questions sur image ou les images occasionnelles. Convertissez l'image au format JPG ou JPEG, encodez-la en Base64, puis envoyez-la après le relâchement du bouton et avant la validation de l'audio :

{
  "type": "input_image_buffer.append",
  "image": "<Base64-encoded JPEG data>"
}
  • Privilégiez les résolutions 480p ou 720p pour des performances optimales. Ne dépassez pas 1080p.
  • L'image encodée en Base64 ne doit pas dépasser 256 Ko. Maintenez l'image source à 190 Ko maximum afin de réserver de l'espace pour l'enveloppe JSON.
  • La piste audio doit avoir envoyé au moins une trame audio lors du tour précédent l'envoi de l'image. L'événement input_audio_buffer.commit suivant valide simultanément les tampons audio et image.

8. Déconnectez et détruisez le moteur

Lorsque la session se termine, déconnectez et détruisez le moteur. Les méthodes disconnect ou destroy ferment automatiquement les périphériques multimédias ; il est donc inutile d'appeler séparément les méthodes d'arrêt. AoqClientEngine est un singleton et ne peut être recréé tant que destroy n'a pas été appelé.

engine.disconnect()
AoqClientEngine.destroy()

Exemple complet

La classe suivante accepte un objet AoqConnectConfig mappé à partir de la réponse du jeton du serveur d'application. En production, ajoutez la gestion de l'état de l'interface utilisateur, des permissions, de la récupération d'erreurs et de la compression d'image.

import Foundation
import AoqClientSdk

final class ManualPushToTalkClient: NSObject, AoqEngineDelegate {
    enum ImageMode: Equatable {
        case none
        case continuousVideo
        case singleImage
    }

    private var engine: AoqClientEngine!
    private let imageMode: ImageMode
    private var hasAudioInCurrentTurn = false

    init(workDir: String, connectConfig: AoqConnectConfig, imageMode: ImageMode) {
        self.imageMode = imageMode
        super.init()
        let createConfig = AoqCreateConfig()
        createConfig.workDir = workDir
        self.engine = AoqClientEngine.createEngine(createConfig, delegate: self)

        // Example values. Match these settings to the selected model and application format.
        let audioEncoderConfig = AoqAudioCodecConfig()
        audioEncoderConfig.trackType = .audio
        audioEncoderConfig.codecType = .audioPCM
        audioEncoderConfig.sampleRate = 16_000
        audioEncoderConfig.channel = 1
        engine.setAudioEncoderConfig(audioEncoderConfig)

        let audioDecoderConfig = AoqAudioCodecConfig()
        audioDecoderConfig.trackType = .audio
        audioDecoderConfig.codecType = .audioPCM
        audioDecoderConfig.sampleRate = 24_000
        audioDecoderConfig.channel = 1
        engine.setAudioDecoderConfig(audioDecoderConfig)

        let publishAudioTrack = AoqTrackParam()
        publishAudioTrack.trackType = .audio
        let publishDataTrack = AoqTrackParam()
        publishDataTrack.trackType = .data
        let subscribeAudioTrack = AoqTrackParam()
        subscribeAudioTrack.trackType = .audio
        let subscribeDataTrack = AoqTrackParam()
        subscribeDataTrack.trackType = .data

        connectConfig.publishTracks = [publishAudioTrack, publishDataTrack]
        connectConfig.subscribeTracks = [subscribeAudioTrack, subscribeDataTrack]

        if imageMode == .continuousVideo {
            let videoEncoderConfig = AoqVideoCodecConfig()
            videoEncoderConfig.trackType = .video
            videoEncoderConfig.codecType = .videoJpeg
            videoEncoderConfig.width = 960
            videoEncoderConfig.height = 540
            videoEncoderConfig.fps = 2
            videoEncoderConfig.bitrate = 500_000
            engine.setVideoEncoderConfig(videoEncoderConfig)

            let publishVideoTrack = AoqTrackParam()
            publishVideoTrack.trackType = .video
            connectConfig.publishTracks = [publishAudioTrack, publishVideoTrack, publishDataTrack]
        }

        let captureConfig = AoqAudioCaptureConfig()
        captureConfig.channel = 1
        captureConfig.isExternal = false
        engine.startAudioCapture(captureConfig)

        let playbackConfig = AoqAudioPlaybackConfig()
        playbackConfig.channel = 1
        playbackConfig.isExternal = false
        playbackConfig.isDefaultSpeaker = true
        engine.startAudioPlayer(playbackConfig)

        if imageMode == .continuousVideo {
            let videoCaptureConfig = AoqVideoCaptureConfig()
            videoCaptureConfig.width = 1280
            videoCaptureConfig.height = 720
            videoCaptureConfig.fps = 15
            engine.startVideoCapture(videoCaptureConfig)
        }

        engine.enableSendMediaStream(.audio, enable: false)
        if imageMode == .continuousVideo {
            engine.enableSendMediaStream(.video, enable: false)
        }
        engine.connect(connectConfig)
    }

    func onPushToTalkPressed() {
        hasAudioInCurrentTurn = true
        engine.enableSendMediaStream(.audio, enable: true)
    }

    func onPushToTalkReleased(base64Jpeg: String? = nil) {
        engine.enableSendMediaStream(.audio, enable: false)
        guard hasAudioInCurrentTurn else { return }
        if imageMode == .singleImage, let base64Jpeg {
            let imageEvent: [String: Any] = [
                "type": "input_image_buffer.append",
                "image": base64Jpeg
            ]
            if let data = try? JSONSerialization.data(withJSONObject: imageEvent) {
                let dataMessage = AoqDataMsg()
                dataMessage.data = data
                engine.sendDataMsg(dataMessage)
            }
        }
        for event in [
            ["type": "input_audio_buffer.commit"],
            ["type": "response.create"]
        ] {
            guard let data = try? JSONSerialization.data(withJSONObject: event) else { continue }
            let dataMessage = AoqDataMsg()
            dataMessage.data = data
            engine.sendDataMsg(dataMessage)
        }
        hasAudioInCurrentTurn = false
    }

    private func sendSessionUpdate() {
        let event: [String: Any] = [
            "type": "session.update",
            "session": [
                "modalities": ["text", "audio"],
                "voice": "Ethan",
                "audio": [
                    "input": ["format": ["type": "pcm", "sample_rate": 16_000]],
                    "output": ["format": ["type": "pcm", "sample_rate": 24_000]]
                ],
                "turn_detection": NSNull()
            ]
        ]
        guard let data = try? JSONSerialization.data(withJSONObject: event) else { return }
        let dataMessage = AoqDataMsg()
        dataMessage.data = data
        engine.sendDataMsg(dataMessage)
    }

    func close() {
        engine.disconnect()
        AoqClientEngine.destroy()
    }

    func onConnectionStatusChange(_ status: AoqConnectionStatus) {
        if status == .connected { sendSessionUpdate() }
    }

    func onDataMsg(_ msg: AoqDataMsg) {
        guard let event = try? JSONSerialization.jsonObject(with: msg.data) as? [String: Any],
              let type = event["type"] as? String else { return }
        if type == "session.updated", imageMode == .continuousVideo {
            engine.enableSendMediaStream(.video, enable: true)
        }
    }

    func onError(_ code: Int, message: String) {}
    func onWarning(_ code: Int, message: String) {}
    func onStats(_ stats: AoqStats) {}
    func onAudioDeviceStateChanged(_ state: AoqAudioDeviceState) {}
    func onAudioDeviceRouteChanged(_ routeType: Int) {}
    func onAudioDeviceInterrupted(_ interrupt: Bool) {}
    func onAudioFileState(_ state: AoqAudioFileState) {}
    func onVideoDeviceStateChanged(_ state: AoqVideoDeviceState) {}
}

Exécution et vérification

Effectuez un tour push-to-talk audio seul, puis un tour incluant une image. Résultats attendus :

  1. La piste audio est désactivée avant l'appui sur le bouton et envoie l'audio en continu tant que le bouton est maintenu enfoncé.
  2. Après le relâchement, les événements input_audio_buffer.committed, response.created et response.done sont reçus séquentiellement, et l'audio généré par le modèle est lu via la piste audio abonnée.
  3. Avec l'option image unique, le modèle répond en utilisant l'image et l'audio du tour. Avec la vidéo continue, il utilise les dernières trames vidéo.

Pour connaître les champs des événements serveur et les schémas de réponse complets, consultez la rubrique Événements serveur.

Points importants

  1. AOQ transporte l'audio via la piste Audio. N'envoyez pas également input_audio_buffer.append.
  2. input_audio_buffer.commit valide uniquement le tour et ne déclenche pas de réponse du modèle. Envoyez ensuite response.create.
  3. Ne validez pas un tampon audio vide. Le service renvoie une erreur.
  4. N'activez pas l'envoi multimédia avant session.updated. En mode manuel, n'activez pas la piste Audio avant que l'utilisateur n'appuie sur le bouton.

Informations connexes

Pour tous les paramètres, champs d'événement et interfaces destinés aux autres plateformes, consultez :