Tous les produits
Search
Centre de documentation

Alibaba Cloud Model Studio:Connexion WebRTC à Qwen-LiveTranslate-Realtime

Dernière mise à jour :Nov 06, 2026

Connexion WebRTC à Qwen-LiveTranslate-Realtime

WebRTC utilise des pistes de médias pour transmettre l’audio et DataChannel pour transmettre les événements du modèle et le texte. Le client web peut utiliser l’API native du navigateur, tandis que les autres clients peuvent utiliser des bibliothèques prenant en charge WebRTC standard. Model Studio ne fournit pas de SDK WebRTC dédié.

Ce document décrit la procédure d’intégration WebRTC de ce modèle et fournit un exemple complet côté navigateur.

Périmètre d’application

Ce document concerne Qwen-LiveTranslate-Realtime. Les guides existants présentent qwen3.8-livetranslate-flash-realtime et qwen3.5-livetranslate-flash-realtime, dont les configurations et champs d’événements diffèrent ; consultez Événements client. Les exemples de ce document utilisent qwen3.8-livetranslate-flash-realtime.

  • Utilisez l’API Key et l’ID de l’espace de travail correspondant à la région et à l’espace de travail sélectionnés.
  • Domaine de Pékin : {WorkspaceId}.cn-beijing.maas.aliyuncs.com ; domaine de Singapour : {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com.
  • Les API Keys à longue durée de validité sont stockées côté serveur et ne doivent pas être incluses dans les applications mobiles ou de navigateur. Pour leur génération et leurs autorisations, consultez Générer une API Key temporaire.

Pour capturer l’audio dans le navigateur, utilisez un contexte sécurisé ; pour utiliser le microphone, demandez l’autorisation correspondante. Lors de la réception audio, gérez les restrictions de lecture automatique du navigateur.

Authentification et échange SDP

Le navigateur envoie une Offer SDP à l’AppServer métier. AppServer transmet la requête suivante avec l’API Key et renvoie l’Answer SDP sans modification au navigateur ; les API Keys à longue durée de validité ne parviennent pas au frontend.

  • Méthode : POST
  • Adresse de Pékin : https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime
  • Adresse de Singapour : https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime
  • En-têtes de requête : Authorization: Bearer <API_KEY>, Content-Type: application/sdp
  • Corps de la requête : chaîne Offer SDP.
  • Réponse réussie : HTTP 200, avec l’Answer SDP dans le corps ; un échec renvoie un code 4xx.

WebRTC effectue l’authentification pendant l’échange SDP et ne nécessite pas de jeton AOQ préalable. Dans les exemples ci-dessous, /api/realtime/sdp désigne une interface personnalisée de l’AppServer métier, qui doit implémenter le transfert décrit ci-dessus ; ce n’est pas une URL fournie par Model Studio.

Déroulement

WEBRTC Diagramme de séquence de connexion
  1. Préparez la clé API et les paramètres du modèle cible sur AppServer.
  2. Créez l’objet de connexion client et enregistrez les rappels.
  3. Configurez les directions des médias et les DataChannels sans attacher les pistes média montantes.
  4. Échangez les SDP via AppServer et attendez que la connexion et le canal d’événements soient prêts.
  5. Configurez le modèle selon son protocole. Attachez les pistes média montantes nécessaires après confirmation.
  6. Arrêtez la capture, fermez la connexion et libérez les ressources à la fin de l’interaction.

Remarque

  • L’audio est transmis sur les pistes de médias WebRTC ; l’application doit obtenir la piste audio locale, associer le lecteur distant, puis envoyer et recevoir les messages DataChannel selon les définitions d’événements du modèle cible.

  • L’authentification WebRTC s’effectue lors de l’échange SDP ; aucun jeton AOQ n’est nécessaire. Relayez l’authentification et les SDP via AppServer pour ne pas exposer la clé API à long terme dans le code frontend.

  • Une connexion réussie indique que le canal de transport est établi, mais ne signifie pas que l’initialisation du modèle est terminée. Avec qwen3.8-livetranslate-flash-realtime utilisé sur cette page, envoyez session.update, puis activez le flux de médias en amont nécessaire après avoir reçu session.updated.

Exemple complet

Voici un exemple complet côté navigateur. Implémentez d’abord l’API /api/realtime/sdp de l’AppServer métier selon Authentification et échange SDP, puis ouvrez cette page HTML depuis la même origine que l’AppServer. Le protocole, l’hôte et le port doivent correspondre. Par exemple, la page et /api/realtime/sdp sont toutes deux servies par http://localhost:3000. Le serveur stocke l’API Key et transfère la SDP ; ne placez pas d’API Keys à longue durée de validité dans la page. Cliquez sur “Finish input” pour envoyer session.finish, attendez session.finished, puis, après la lecture de l’audio restant, cliquez sur “Disconnect after audio playback”. Pendant la connexion ou pour quitter plus tôt, cliquez sur “Cancel and disconnect” ; cette opération ferme immédiatement la connexion et peut entraîner la perte des traductions non encore reçues ou de l’audio non encore lu. Cet exemple établit une connexion à la fois ; rechargez la page pour le réutiliser.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>WebRTC access example</title>
</head>
<body>
<button id="start-session" type="button">Start translation</button>
<button id="finish-input" type="button" disabled>Finish input</button>
<button id="end-session" type="button" disabled>Disconnect after audio playback</button>
<button id="cancel-session" type="button" disabled>Cancel and disconnect</button>
<p id="session-status" role="status" aria-live="polite">Click "Start translation" to connect.</p>
<script>
const startSessionButton = document.getElementById("start-session");
const finishInputButton = document.getElementById("finish-input");
const endSessionButton = document.getElementById("end-session");
const cancelSessionButton = document.getElementById("cancel-session");
const sessionStatus = document.getElementById("session-status");
let translationFinished = false;
const pc = new RTCPeerConnection({ iceServers: [ ] });

const channels = new Set();
const senders = new Map();
const remoteAudio = document.createElement("audio");
remoteAudio.controls = true; // Users can play audio manually if autoplay is blocked.
remoteAudio.autoplay = true;
document.body.appendChild(remoteAudio);

let localStream = null;
let eventChannel = null;
let stopped = false;
let started = false;
let sessionCreated = false;
let updateSent = false;
let modelReady = false;
let mediaStarted = false;
let finishRequested = false;
const requestController = new AbortController();
const media = {
  sendAudio: true,
  receiveAudio: true,
};

function mediaDirection(send, receive) {
  if (send && receive) return "sendrecv";
  if (send) return "sendonly";
  if (receive) return "recvonly";
  return "inactive";
}

async function configureMedia() {
  // Keep m=audio. The target service must support the selected directions.
  const transceiver = pc.addTransceiver("audio", {
    direction: mediaDirection(media.sendAudio, media.receiveAudio),
  });
  if (media.sendAudio) senders.set("audio", transceiver.sender);
  if (!media.sendAudio) return;
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: media.sendAudio,
    video: false,
  });
  // The user may end the call while the permission prompt is open.
  if (stopped) {
    stream.getTracks().forEach(track => track.stop());
    throw new Error("Session ended");
  }
  localStream = stream;
  // sender.track is still empty, so no captured data is sent to the model.
}
// See Send and receive events over DataChannels for bindDataChannel.
pc.ondatachannel = ({ channel }) => bindDataChannel(channel);
const clientChannel = pc.createDataChannel("oai-events");
bindDataChannel(clientChannel);
pc.onconnectionstatechange = () => {
  console.log("WebRTC state:", pc.connectionState);
  if (pc.connectionState === "connected") {
    tryInitializeModel();
  } else if (["failed", "disconnected", "closed"].includes(pc.connectionState)) {
    // This example ends the session; implement recovery as needed.
    endSession();
  }
};

function waitForIceComplete() {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => finish(new Error("ICE gathering timed out")), 15000);
    function finish(error) {
      clearTimeout(timer);
      pc.removeEventListener("icegatheringstatechange", check);
      requestController.signal.removeEventListener("abort", cancel);
      error ? reject(error) : resolve();
    }
    function check() {
      if (pc.iceGatheringState === "complete") finish();
    }
    function cancel() { finish(new Error("Session ended")); }
    pc.addEventListener("icegatheringstatechange", check);
    requestController.signal.addEventListener("abort", cancel, { once: true });
    if (stopped) cancel();
    else check();
  });
}

function normalizeAnswerSdp(sdp) {
  return String(sdp).trim().replace(/\r?\n/g, "\r\n") + "\r\n";
}

async function startSession() {
  if (started || stopped) return;
  started = true;
  try {
    await configureMedia();
    if (stopped) return;
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    await waitForIceComplete();

    // Implement this same-origin application endpoint; it is not a Model Studio API.
    // AppServer forwards SDP with the API key and returns the raw Answer SDP.
    const response = await fetch("/api/realtime/sdp", {
      method: "POST",
      headers: { "Content-Type": "application/sdp" },
      body: pc.localDescription.sdp,
      signal: requestController.signal,
    });
    if (!response.ok) {
      throw new Error(`SDP exchange failed:${response.status} ${await response.text()}`);
    }
    const answerSdp = normalizeAnswerSdp(await response.text());
    if (stopped) return;
    await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
    // Continue through connectionstatechange, DataChannel, and model events.
  } catch (error) {
    if (!stopped) console.error("Connection failed:", error);
    endSession();
  }
}
function tryInitializeModel() {
  if (stopped || updateSent || !sessionCreated ||
      pc.connectionState !== "connected" ||
      eventChannel?.readyState !== "open") return;
  sendModelEvent({
    event_id: `event_${crypto.randomUUID()}`,
    type: "session.update",
    session: {
      "output_modalities": media.receiveAudio ? ["text", "audio"] : ["text"],
      "audio": {
        "input": {
          "turn_detection": {
            "type": "speaker_detection",
            "threshold": 0.5,
          },
        },
      },
      ...(media.receiveAudio ? {
        "voice": "Tina",
      } : {}),
      "translation": {
        "language": "en",
      },
    },
  });
  updateSent = true;
}

async function handleModelEvent(event, channel) {
  if (stopped) return;
  if (event.type === "session.created") {
    sessionCreated = true;
    eventChannel = channel; // Send configuration through the channel that receives session events.
    tryInitializeModel();
  } else if (event.type === "session.updated" && updateSent) {
    modelReady = true;
    await startSendingMedia();
    if (!finishRequested && !stopped) {
      finishInputButton.disabled = false;
      sessionStatus.textContent = "The model is ready. You can start speaking.";
    }
  } else if (event.type === "session.finished") {
    translationFinished = true;
    finishInputButton.disabled = true;
    endSessionButton.disabled = false;
    sessionStatus.textContent = "Translation is complete. Wait for remaining audio to finish before disconnecting.";
    console.log("Translation processing has ended. Wait for remaining audio to finish before releasing resources.");
  } else if (event.type === "error") {
    console.error("Model error:", event.error);
    endSession();
  } else {
    // Display text, transcripts, or response status as defined by the selected model. Receive audio over media tracks.
    console.log("Model event:", event);
  }
}
async function startSendingMedia() {
  if (stopped || !modelReady || mediaStarted) return;
  mediaStarted = true;

  for (const track of localStream?.getTracks() ?? []) {

    if (stopped) return;
    const sender = senders.get(track.kind);
    if (sender) await sender.replaceTrack(track);
  }
}

// Register before startSession(). Tracks without streams are also received correctly.
const remoteAudioStream = new MediaStream();
pc.ontrack = ({ track }) => {
  if (stopped) return;
  if (track.kind === "audio" && media.receiveAudio) {
    remoteAudioStream.addTrack(track);
    remoteAudio.srcObject = remoteAudioStream;
    remoteAudio.play().catch(() => {
      console.info("Autoplay is blocked. Use the audio controls to play.");
    });
  }
};
async function finishTranslation() {
  if (stopped || !modelReady || finishRequested) return;
  finishRequested = true;
  try {
    localStream?.getAudioTracks().forEach(track => track.stop());
    const sender = senders.get("audio");
    if (sender) await sender.replaceTrack(null);
    sendModelEvent({ type: "session.finish" });
  } catch (error) {
    finishRequested = false;
    throw error;
  }
}
function endSession() {
  if (stopped) return;
  stopped = true;
  finishInputButton.disabled = true;
  endSessionButton.disabled = true;
  cancelSessionButton.disabled = true;
  sessionStatus.textContent = "The connection is closed. Reload the page to translate again.";
  modelReady = false;
  sessionCreated = false;
  updateSent = false;
  mediaStarted = false;
  requestController.abort(); // Cancel pending SDP requests and ICE gathering.
  localStream?.getTracks().forEach(track => track.stop());
  localStream = null;
  channels.forEach(channel => channel.close());
  channels.clear();
  eventChannel = null;
  pc.close();
  senders.clear();
  remoteAudioStream.getTracks().forEach(track => track.stop());
  remoteAudio.pause();
  remoteAudio.srcObject = null;
  remoteAudio.remove();
}
function sendModelEvent(event, channel = eventChannel) {
  if (stopped || pc.connectionState !== "connected" ||
      channel?.readyState !== "open") {
    throw new Error("Data channel is not ready");
  }
  channel.send(JSON.stringify(event));
}

function bindDataChannel(channel) {
  channels.add(channel);
  channel.onopen = () => {
    if (stopped) return;
    console.log("DataChannel opened:", channel.label);
    tryInitializeModel();
  };
  channel.onmessage = ({ data }) => {
    if (stopped) return;
    let event;
    try { event = JSON.parse(data); }
    catch (error) {
      console.warn("Cannot parse model event:", error);
      return;
    }
    if (!event || typeof event !== "object") return;
    handleModelEvent(event, channel).catch(error => {
      if (!stopped) console.error("Model event handling failed:", error);
      endSession();
    });
  };
  channel.onerror = error => console.error("DataChannel error:", error);
  channel.onclose = () => {
    channels.delete(channel);
    if (channel === eventChannel) endSession();
  };
  if (channel.readyState === "open") channel.onopen();
}

startSessionButton.addEventListener("click", () => {
  startSessionButton.disabled = true;
  cancelSessionButton.disabled = false;
  sessionStatus.textContent = "Connecting...";
  startSession();
});
finishInputButton.addEventListener("click", () => {
  finishInputButton.disabled = true;
  sessionStatus.textContent = "Finishing input...";
  finishTranslation().then(() => {
    if (!translationFinished && !stopped) {
      sessionStatus.textContent = "Input has ended. Waiting for translation to complete.";
    }
  }).catch(error => {
    if (!stopped) {
      finishInputButton.disabled = false;
      sessionStatus.textContent = "Unable to finish input. Try again.";
    }
    console.error("Failed to finish input:", error);
  });
});
endSessionButton.addEventListener("click", endSession);
cancelSessionButton.addEventListener("click", endSession);
</script>
</body>
</html>

1. Préparer l’authentification et les paramètres de connexion

AppServer configure l’échange SDP selon le modèle cible, la région et l’espace de travail. Les paramètres suivants concernent les connexions Realtime standard. Pour les paramètres propres à une application, suivez la bonne pratique correspondante.

SettingValue
MéthodePOST
URLhttps://{endpoint}/api/v1/webrtc/realtime?model={model_name}
Content-Typeapplication/sdp
AuthorizationBearer <API_KEY>
Corps de la requêteOffer SDP
Réponse réussieAnswer SDP

Pour les régions, les adresses et l’authentification, consultez Authentification et échange SDP. L’interface de l’AppServer métier est implémentée par l’entreprise et ne fait pas partie de l’API SDK de Model Studio.

2. Initialiser l’objet de connexion et enregistrer les callbacks

Côté web, créez une RTCPeerConnection ; sur les autres plateformes, initialisez la bibliothèque correspondante. Avant d’établir la connexion, enregistrez les gestionnaires suivants :

  • Changements d’état de la connexion : déterminez si la connexion a réussi, a échoué ou a été interrompue.

  • Création, ouverture, messages et fermeture de DataChannel : transporte les événements du modèle.

  • Piste de médias distante : reçoit et lit l’audio, ou traite les autres médias pris en charge par l’application cible.

L’exemple complet crée l’objet de connexion, les variables d’état et les callbacks dans le même script. Chaque appel crée une seule connexion ; une fois l’appel terminé, réinitialisez ces objets pour effectuer un autre appel.

L’objet de connexion et les variables d’état sont détaillés dans Exemple complet.

L’exemple complet utilise finishRequested pour éviter d’envoyer plusieurs fois l’événement de fin.

Enregistrez les écouteurs de messages avant l’initialisation du modèle pour éviter de manquer les événements de session envoyés spontanément par le serveur. N’écoutez pas uniquement les canaux créés par le client : le serveur transmet les événements sur un canal nommé txt ; vous devez donc traiter le callback datachannel.

3. Configurer les directions des médias et les DataChannels

Sélectionner la direction des médias

Les directions sont relatives au client. Les émetteurs-récepteurs WebRTC négocient l’envoi et la réception : sendrecv est bidirectionnel, sendonly envoie uniquement et recvonly reçoit uniquement. Les capacités disponibles dépendent aussi de la réponse SDP du serveur et de la prise en charge du modèle.

DirectionConfiguration
Envoyer l’audioNégociez l’envoi audio et préparez le microphone ou une piste audio externe. Ajoutez la piste une fois le modèle prêt.
Recevoir l’audioNégociez la réception audio et configurez la lecture dans le callback de réception d’une piste distante.
Envoyer la vidéoNégociez l’envoi vidéo selon la version sélectionnée et les scénarios pris en charge, puis préparez la caméra ou une piste vidéo externe.

Par exemple, configurez l’envoi audio en amont pour une entrée vocale et la réception audio en aval lorsqu’une traduction vocale est nécessaire. L’Offer SDP doit inclure la section de média audio m=audio ; ne la supprimez pas simplement parce que l’application n’a pas besoin de lire l’audio.

Directions des médias pour ce modèle

Modèle ou applicationEnvoyer de l’audioRecevoir de l’audioEnvoyer de la vidéoRecevoir de la vidéo
Qwen-LiveTranslate-Realtime Activez lorsque l’entrée vocale est utilisée Activez lorsque la traduction vocale est nécessaire Configurez selon la version et le scénario sélectionnés Activation non prise en charge

Ce modèle produit du texte ou de l’audio, mais pas de vidéo.

Ce tableau décrit les directions des médias nécessaires à l’application ; il ne garantit pas que toutes les combinaisons puissent être négociées indépendamment. La configuration dépend du modèle cible et des capacités de négociation du serveur.

Préparer les médias sans les envoyer

Négociez les directions requises avant de créer l’Offer. Si le modèle nécessite une confirmation d’initialisation, réservez les capacités d’envoi sans attacher les pistes capturées. Lorsque le modèle est prêt, utilisez replaceTrack sur l’émetteur correspondant pour démarrer la transmission. Enregistrez à l’avance les gestionnaires de réception.

Configurez séparément l’envoi et la réception audio selon les besoins ; ne regroupez pas l’activation du microphone, la lecture audio et l’initialisation du modèle dans une seule opération.

Exemple de configuration des médias

L’exemple suivant utilise une conversation vocale bidirectionnelle. Les deux options dans media contrôlent respectivement l’envoi et la réception audio. addTransceiver négocie la direction à l’avance ; la piste capturée reste d’abord stockée localement et n’est associée à l’émetteur qu’une fois le modèle prêt.

Consultez le code dans l’exemple complet.

Créer les canaux d’événements

Créez un DataChannel avant de créer une Offer pour que la SDP inclue la négociation du canal de données. Consultez Envoi et réception d’événements sur le canal Data pour savoir comment le client et le serveur créent les canaux.

DataChannel envoie les événements d’initialisation du modèle, les événements textuels et de contrôle, et reçoit les résultats, les états et les erreurs du modèle. La piste de médias transmet l’audio.

L’exemple complet crée un canal d’événements client et enregistre le callback du canal serveur avant de créer une Offer.

4. Établir la connexion

Effectuez la connexion dans l’ordre suivant :

  1. Appelez createOffer et setLocalDescription.
  2. Collectez les informations sur les candidats selon les méthodes ICE prises en charge par le serveur ; dans cet exemple, la SDP locale est envoyée une fois la collecte ICE terminée.
  3. Envoyez la SDP locale à l’AppServer métier, qui utilise l’API Key pour appeler l’interface d’échange SDP de Model Studio.
  4. Vérifiez l’état HTTP et, en cas de succès, appelez setRemoteDescription avec l’Answer SDP renvoyée.
  5. Attendez que l’état de la connexion indique un succès et confirmez que le DataChannel utilisé pour envoyer les événements du modèle est ouvert.

La réussite de l’échange SDP ou la fin de setRemoteDescription indique uniquement que l’étape de négociation correspondante est terminée ; elle ne prouve pas que la connexion des médias a réussi. Les événements du modèle ne peuvent pas non plus être envoyés si DataChannel n’est pas à l’état open.

Code de connexion et callbacks d’état

Écoutez connectionstatechange pour déterminer l’état de la connexion et icegatheringstatechange pour attendre la SDP locale complète. L’initialisation du modèle nécessite également l’état open de DataChannel et l’événement de disponibilité du modèle ; elle ne doit pas dépendre uniquement de la fin de l’échange SDP.

L’exemple complet effectue l’échange SDP avec startSession() et traite le résultat de la connexion dans le callback d’état de la connexion.

L’AppServer métier doit transférer l’Offer selon l’adresse et les en-têtes de requête de la section 1 ; l’API d’exemple doit renvoyer du texte SDP brut. Ne transmettez pas directement à setRemoteDescription du contenu incluant des en-têtes de réponse HTTP, des journaux ou des enveloppes JSON.

5. Échanger les événements du modèle et les médias

Initialiser le modèle

Lorsque la connexion et le canal d’événements sont disponibles, initialisez le modèle cible selon son protocole. Conservez les événements d’initialisation serveur et combinez-les avec l’état du canal pour avancer ; ne supposez pas un ordre fixe d’arrivée des rappels.

Par exemple, lorsque vous configurez un modèle avec session.update, construisez l’événement selon son protocole et attendez la confirmation de configuration avant d’envoyer les entrées. Les paramètres de voix, de modalités de sortie, de VAD et de fréquence d’échantillonnage doivent tous provenir de la documentation du modèle actuel.

session.created indique la création de la session ; il ne signifie pas systématiquement que la configuration est appliquée. Pour les modèles exigeant une confirmation de la configuration de session, attendez session.updated.

Consultez Événements client et Événements serveur pour les définitions d’événements du modèle. Gérez la transmission des médias et les modes disponibles comme décrit sur cette page.

Exemples d’événements du modèle

RemarqueL’audio est transmis sur la piste de médias et n’utilise pas l’événement WebSocket d’envoi des médias.

L’exemple suivant présente le déroulement session.created → session.update → session.updated pour qwen3.8-livetranslate-flash-realtime. Configurez les champs selon la référence des événements du modèle. N’associez la piste de médias en amont qu’après avoir reçu session.updated, qui confirme la fin de la configuration.

La configuration suivante traduit la parole vers l’anglais avec la voix Tina.

Consultez le code dans l’exemple complet.

Pour le modèle d’exemple qwen3.8-livetranslate-flash-realtime, lorsque la sortie audio est activée, response.audio.done permet de vérifier l’état de génération audio du modèle ; cet événement ne signifie pas que la lecture audio du client est terminée.

Démarrer la transmission et la réception des médias

Une fois le modèle prêt, associez la piste audio en amont nécessaire. Traitez la sortie du modèle dans le callback de piste distante et analysez en continu les événements renvoyés par DataChannel.

  • L’audio transite par les pistes média RTP ; aucun événement input_audio_buffer.append n’est nécessaire.

  • Les DataChannels transportent le texte et les événements d’état, de contrôle et d’erreur.

Dans l’exemple complet, startSendingMedia() associe la piste en amont et ontrack traite les médias reçus.

6. Terminer l’interaction et libérer les ressources

Pour le modèle d’exemple qwen3.8-livetranslate-flash-realtime, une fois l’entrée audio terminée, arrêtez la capture et la transmission audio, puis envoyez session.finish via DataChannel. Continuez à recevoir la dernière phrase dans sa forme originale, sa traduction et l’audio traduit, puis attendez session.finished. Cet événement indique que le modèle a terminé le traitement de la traduction, mais ne signifie pas que le client a fini de lire l’audio traduit restant ; si une lecture complète est nécessaire, appelez endSession() pour libérer les ressources après la lecture.

Dans l’exemple complet, finishTranslation() utilise la piste audio et le DataChannel créés. Si l’entrée provient d’un fichier ou d’un tampon métier, envoyez d’abord l’audio en mémoire tampon avant de terminer l’entrée.

L’implémentation de finishTranslation() figure dans Exemple complet.

endSession() libère uniquement la connexion et les ressources de médias ; il n’envoie pas d’événement de fin.

Après l’interaction avec le modèle, la couche métier du client arrête la piste de capture locale détenue par l’application, ferme DataChannel et RTCPeerConnection, libère les ressources telles que le lecteur, puis nettoie l’état métier.

Effacez l’état de disponibilité du modèle après un échec ou une déconnexion inattendue. Initialisez à nouveau le modèle sur une nouvelle connexion au lieu de réutiliser l’état de session précédent.

Dans l’exemple complet, endSession() libère la connexion et les ressources de médias.

Appelez endSession() après l’interaction avec le modèle et le traitement de l’audio traduit restant. Si l’application utilise également l’enregistrement, arrêtez aussi MediaRecorder.

Envoyer et recevoir des événements sur le canal Data

Vérifiez que le canal est open avant d’appeler send avec un événement sérialisé. Analysez les messages entrants dans son rappel message, y compris sur les canaux créés par le serveur.

Les noms, champs, paramètres et moments d’envoi des événements suivent les définitions des événements client du modèle cible ; l’analyse, les changements d’état, les résultats et la gestion des erreurs après réception suivent les définitions des événements serveur du modèle. Les événements d’initialisation sont envoyés selon les exigences du modèle, puis les événements textuels et de contrôle selon les besoins métier.

Exemple DataChannel

Utilisez la même fonction de liaison pour les canaux créés par le client et le serveur. Elle gère le transport et l’analyse JSON ; handleModelEvent de la section 5 traite la sémantique du modèle. Utilisez sendModelEvent pour l’initialisation, le texte ou le contrôle uniquement lorsque le protocole du modèle le permet.

Dans l’exemple complet, sendModelEvent() et bindDataChannel() envoient et reçoivent les événements.

Dans l’exemple complet, startSession() est appelée au début de la traduction, finishTranslation() à la fin de l’entrée et endSession() après la traduction, la lecture et le nettoyage. Ces fonctions sont des exemples définis dans ce document et ne font pas partie des API WebRTC intégrées.

Définitions des événements Qwen-LiveTranslate-Realtime

Modèle ou applicationÉvénements client (envoi)Événements serveur (réception)
Qwen-LiveTranslate-RealtimeÉvénements clientÉvénements serveur

Respectez également les règles suivantes :

  • Utilisez le champ type des événements pour identifier les événements du modèle et traitez les identifiants associés selon la référence des événements de ce modèle.

  • Les opérations requises par le modèle, telles que la confirmation de l’initialisation, la soumission des entrées, l’annulation des réponses et la fin des tâches, sont toutes traitées sur le canal Data selon le protocole correspondant ; elles définissent les interactions du modèle et sont distinctes de la déconnexion du transport.

  • Les données audio sont transmises sur les pistes de médias. Lorsque vous utilisez la référence des événements du modèle, suivez cette page pour l’encapsulation des médias et les modes d’interaction disponibles. N’appliquez pas directement la méthode WebSocket d’envoi des médias.

Références des événements