Tous les produits
Search
Centre de documentation

Alibaba Cloud Model Studio:Connexion WebRTC

Dernière mise à jour :Oct 11, 2026

Découvrez le flux WebRTC : préparer l’authentification, configurer les directions des médias, échanger les SDP et événements du modèle, et libérer les ressources.

WebRTC transporte l’audio et la vidéo sur les pistes média, et les événements du modèle et le texte via les DataChannels. Les applications Web peuvent utiliser les API natives du navigateur ; les autres plateformes peuvent utiliser des bibliothèques WebRTC standard. Model Studio ne fournit pas de SDK WebRTC dédié.

Prérequis

  • Effectuez les préparatifs de la Présentation des connexions.

  • Consultez la Présentation de Realtime API pour vérifier la prise en charge de WebRTC par votre modèle cible. Les catégories prises en charge comprennent l’interaction multimodale en temps réel, les conversations vocales, la traduction vocale et la suite d’interaction multimodale. Pour la synthèse ou la reconnaissance vocale, utilisez AOQ ou WebSocket selon la prise en charge du modèle.

  • Préparez sur votre serveur d’application (AppServer) une clé API pour la région et l’espace de travail. AppServer relaie l’échange SDP. Pour les champs d’authentification, consultez Authentification par jeton.

RemarqueUtilisez un contexte sécurisé dans le navigateur et demandez les autorisations du microphone ou de la caméra selon les besoins. Gérez les restrictions de lecture automatique lors de la réception audio.

Flux de travail

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

  • Les pistes média WebRTC transportent l’audio et la vidéo. Votre application obtient les pistes locales, associe les lecteurs distants et échange 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 signifie que le transport est établi, mais il faut attendre la réussite de l’initialisation du modèle cible avant d’envoyer des médias. Par exemple, les modèles Realtime doivent attendre session.updated. Le client ne peut activer les médias montants nécessaires qu’après la réussite de l’initialisation du modèle.

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.

ParamètreValeur
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, points de terminaison et informations d’authentification, consultez Authentification par jeton. Implémentez l’interface AppServer dans votre application ; elle ne fait pas partie des API du SDK Model Studio.

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

Créez un RTCPeerConnection dans le navigateur, ou initialisez la bibliothèque correspondante sur une autre plateforme. Avant la connexion, enregistrez des gestionnaires pour :

  • Les changements d’état de connexion, notamment la réussite, l’échec et la déconnexion.

  • La création, l’ouverture, les messages et la fermeture des DataChannels.

  • Les pistes média distantes pour recevoir et lire l’audio ou la vidéo.

const pc = new RTCPeerConnection({ iceServers: [ ] });

const channels = new Set();
const senders = new Map();
const remoteAudio = document.createElement("audio");
remoteAudio.controls = true; // Allow manual playback 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;
const requestController = new AbortController();

Enregistrez les écouteurs de messages avant d’initialiser le modèle pour ne pas manquer les événements de session envoyés par le serveur. N’écoutez pas uniquement le canal créé par le client : le serveur envoie des événements sur un canal nommé txt, vous devez donc traiter aussi le rappel datachannel.

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

Sélectionner les directions 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 de l’audioNégocier l’envoi audio et préparer une piste microphone ou audio externe ; l’attacher lorsque le modèle est prêt
Recevoir de l’audioNégocier la réception audio et associer la lecture dans le rappel de piste distante
Envoyer de la vidéoNégocier l’envoi vidéo uniquement si le modèle accepte une entrée visuelle ; préparer une piste caméra ou vidéo externe
Recevoir de la vidéoNégocier la réception et implémenter le rendu uniquement si le modèle ou l’application prend explicitement en charge la sortie vidéo

Les conversations vocales nécessitent généralement un audio bidirectionnel ; une entrée visuelle nécessite également une vidéo montante. L’Offer SDP doit contenir une section média m=audio. Ne la supprimez pas simplement parce que l’application n’a pas besoin de lecture audio.

Choisir les directions pour votre modèle

Modèle ou applicationEnvoyer de l’audioRecevoir de l’audioEnvoyer de la vidéoRecevoir de la vidéo
Qwen-Omni-RealtimePour l’entrée vocalePour les réponses oralesPour l’entrée visuelleNon pris en charge
Qwen-Audio-RealtimePour les conversations vocalesPour les réponses oralesNon pris en chargeNon pris en charge
Qwen-LiveTranslate-RealtimePour l’entrée vocalePour les traductions oralesSelon la version et le scénarioNon pris en charge
multimodal-dialogSelon l’entrée de l’applicationSelon la sortie de l’applicationSelon les capacités de l’applicationUniquement si l’application le prend explicitement en charge

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 indépendamment l’envoi audio, la réception audio, l’envoi vidéo et la réception vidéo. L’ouverture du microphone, la lecture audio et l’initialisation du modèle sont des opérations distinctes.

Exemple de configuration des médias

Cet exemple utilise un audio bidirectionnel et une vidéo montante facultative. Les quatre commutateurs media expriment les besoins de l’application. Activez la réception vidéo uniquement si l’application cible la prend explicitement en charge. addTransceiver négocie les directions à l’avance ; les pistes capturées restent locales jusqu’à leur rattachement aux émetteurs après la disponibilité du modèle.

const media = {
  sendAudio: true,
  receiveAudio: true,
  sendVideo: false,
  receiveVideo: false,
};

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

async function configureMedia() {
  for (const kind of ["audio", "video"]) {
    const send = kind === "audio" ? media.sendAudio : media.sendVideo;
    const receive = kind === "audio" ? media.receiveAudio : media.receiveVideo;
    // Keep m=audio; the target service must support the selected directions.
    if (kind === "audio" || send || receive) {
      const transceiver = pc.addTransceiver(kind, {
        direction: mediaDirection(send, receive),
      });
      if (send) senders.set(kind, transceiver.sender);
    }
  }
  if (!media.sendAudio && !media.sendVideo) return;
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: media.sendAudio,
    video: media.sendVideo ? {
      width: { ideal: 640 }, height: { ideal: 480 },
      frameRate: { ideal: 2, max: 2 },
    } : false,
  });
  // The user may end the session 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 null; captured data is not sent to the model.
}

Définissez la résolution vidéo et la fréquence d’images selon le modèle cible. Si l’aperçu local et le flux montant nécessitent des fréquences différentes, consultez l’implémentation Canvas dans la bonne pratique.

Créer les canaux d’événements

Créez un DataChannel avant l’Offer afin que le SDP inclue sa négociation. Pour les canaux créés par le client et le serveur, consultez les bonnes pratiques WebRTC ci-dessous.

Les DataChannels transportent l’initialisation du modèle, le texte et les événements de contrôle, et renvoient les résultats, états et erreurs. Les pistes média transportent l’audio et la vidéo.

// See Send and receive events over DataChannels for bindDataChannel.
pc.ondatachannel = ({ channel }) => bindDataChannel(channel);
const clientChannel = pc.createDataChannel("oai-events");
bindDataChannel(clientChannel);

4. Établir la connexion

  1. Appelez createOffer et setLocalDescription.
  2. Collectez les candidats avec un mode ICE pris en charge par le serveur. Cet exemple attend la fin de la collecte ICE avant d’envoyer le SDP local.
  3. Envoyez le SDP local à AppServer, qui appelle le point de terminaison d’échange SDP de Model Studio avec la clé API.
  4. Vérifiez l’état HTTP. En cas de réussite, transmettez l’Answer SDP reçu à setRemoteDescription.
  5. Attendez la connexion et vérifiez que le DataChannel d’envoi des événements du modèle est ouvert.

Un échange SDP réussi ou l’achèvement de setRemoteDescription ne termine que cette étape de négociation ; cela ne signifie pas que la connexion média est prête. Les événements ne peuvent être envoyés que lorsque le DataChannel est open.

Code de connexion et rappels d’état

Écoutez connectionstatechange pour déterminer l’état de connexion et icegatheringstatechange pour attendre un SDP local complet. L’initialisation du modèle dépend aussi de l’ouverture du DataChannel et des événements du modèle ; l’échange SDP seul ne suffit pas.

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();
  }
}

AppServer doit transmettre l’Offer avec le point de terminaison et les en-têtes de la section 1. L’interface de l’exemple renvoie du SDP en texte brut. Ne transmettez pas d’en-têtes HTTP, de journaux ni d’enveloppe JSON à setRemoteDescription.

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, les modèles configurés avec session.update nécessitent un événement conforme à leur protocole et une confirmation avant l’envoi d’entrées. Consultez la documentation actuelle du modèle pour la voix, les modalités de sortie, le VAD et les taux d’échantillonnage. La suite d’interaction multimodale utilise son propre protocole d’événements.

session.created signifie que la session a été créée, pas nécessairement que la configuration est appliquée. Pour les modèles nécessitant cette confirmation, attendez session.updated. Les autres modèles ou applications ont leurs propres conditions de disponibilité.

La navigation des modèles dans la Présentation des connexions WebSocket mène aux définitions d’événements. Réutilisez uniquement ces définitions ; le transport des médias et les restrictions WebRTC suivent toujours cette page.

Exemple d’événements du modèle : Omni

Cet exemple illustre le flux Omni session.created → session.update → session.updated. Pour les autres modèles, adaptez l’événement d’initialisation et le traitement des réponses selon leurs définitions référencées ci-dessous. Attachez les pistes montantes seulement après la confirmation de configuration par session.updated.

function tryInitializeModel() {
  if (stopped || updateSent || !sessionCreated ||
      pc.connectionState !== "connected" ||
      eventChannel?.readyState !== "open") return;
  sendModelEvent({
    event_id: `event_${crypto.randomUUID()}`,
    type: "session.update",
    session: {
      modalities: media.receiveAudio ? ["text", "audio"] : ["text"],
      input_audio_format: "pcm",
      output_audio_format: "pcm",
      turn_detection: { type: "server_vad", threshold: 0.5,
                        silence_duration_ms: 800 },
    },
  });
  updateSent = true;
}

async function handleModelEvent(event, channel) {
  if (stopped) return;
  if (event.type === "session.created") {
    sessionCreated = true;
    eventChannel = channel; // Send configuration on the channel that delivered the session event.
    tryInitializeModel();
  } else if (event.type === "session.updated" && updateSent) {
    modelReady = true;
    await startSendingMedia();
  } else if (event.type === "error") {
    console.error("Model error:", event.error);
    endSession();
  } else {
    // Handle text, transcripts, and response state; receive audio on media tracks.
    console.log("Model event:", event);
  }
}

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

Lorsque le modèle est prêt, attachez les pistes audio et vidéo nécessaires à l’envoi. Traitez les sorties du modèle dans le rappel de piste distante et continuez à analyser les événements DataChannel.

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

  • Les images transitent par les pistes vidéo. WebRTC ne prend pas en charge input_image_buffer.append.

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

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(); also handle tracks with no streams.
const remoteAudioStream = new MediaStream();
const remoteVideo = media.receiveVideo ? document.createElement("video") : null;
if (remoteVideo) {
  remoteVideo.autoplay = true;
  remoteVideo.playsInline = true;
  remoteVideo.controls = true;
  document.body.appendChild(remoteVideo);
}
pc.ontrack = ({ track }) => {
  if (stopped) return;
  if (track.kind === "audio" && media.receiveAudio) {
    remoteAudioStream.addTrack(track);
    remoteAudio.srcObject = remoteAudioStream;
    remoteAudio.play().catch(() => {
      console.info("Autoplay blocked. Use the audio controls to play.");
    });
  }
  if (track.kind === "video" && remoteVideo) {
    remoteVideo.srcObject = new MediaStream([track]);
    remoteVideo.play().catch(() => console.info("Use the video controls to play."));
  }
};

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

Lorsque la connexion n’est plus nécessaire, arrêtez les pistes de capture locales de votre application, fermez les DataChannels et le RTCPeerConnection, libérez les lecteurs et effacez l’état de l’application.

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.

function endSession() {
  if (stopped) return;
  stopped = true;
  modelReady = false;
  sessionCreated = false;
  updateSent = false;
  mediaStarted = false;
  requestController.abort(); // Cancel the pending SDP request and ICE wait.
  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();
  if (remoteVideo) {
    remoteVideo.srcObject?.getTracks().forEach(track => track.stop());
    remoteVideo.pause();
    remoteVideo.srcObject = null;
    remoteVideo.remove();
  }
}

Appelez endSession() lorsque l’utilisateur termine l’appel ou quitte la page. Si l’application ajoute les fonctionnalités Canvas ou d’enregistrement de la bonne pratique, annulez également les boucles d’animation et arrêtez les pistes média Canvas ainsi que le MediaRecorder.

Envoyer et recevoir des événements sur les DataChannels

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.

Respectez les définitions des événements client du modèle cible pour les noms, champs, paramètres et moments d’envoi. Respectez ses définitions des événements serveur pour analyser les réponses, changements d’état, résultats et erreurs. Envoyez les événements d’initialisation requis par le modèle, puis les événements textuels et de contrôle nécessaires à votre application.

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.

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();
}

Placez tous les fragments dans le même script. Après avoir enregistré les déclarations et rappels, appelez startSession() depuis le gestionnaire du bouton de démarrage et endSession() depuis celui du bouton de fin. Ces fonctions appartiennent à l’exemple, pas aux API natives de WebRTC.

Définitions des événements du modèle

Modèle ou applicationÉvénements client (envoi)Événements serveur (réception)
Qwen-Omni-RealtimeÉvénements clientÉvénements serveur
Qwen-Audio-RealtimeÉvénements clientÉvénements serveur
Qwen-LiveTranslate-RealtimeÉvénements clientÉvénements serveur
multimodal-dialogInput Message dans le protocole d’interactionOutput Message dans le protocole d’interaction

La suite d’interaction multimodale définit les deux directions dans un même protocole. Sélectionnez les définitions correspondant à la version du modèle utilisée ; les modèles ne partagent pas tous un schéma d’événements unique.

Respectez également les règles suivantes :

  • Identifiez les types d’événements et les identifiants de corrélation selon le modèle. Realtime utilise généralement type ; la suite d’interaction multimodale utilise header.action pour les événements client, header.event pour les événements serveur et task_id pour la corrélation.

  • Gérez la confirmation d’initialisation, la soumission des entrées, l’annulation des réponses et la fin des tâches sur les DataChannels conformément au protocole. Ces opérations sont distinctes de la fermeture de la connexion de transport.

  • Les médias utilisent les pistes média. Lorsque vous consultez la documentation des événements du modèle, suivez cette page WebRTC pour le transport et les modes d’interaction disponibles ; ne reprenez pas les mécanismes d’envoi de médias de WebSocket.

Bonnes pratiques

  • Appels Omni en temps réel via WebRTC : interface utilisateur, enregistrement et réduction de la fréquence d’images avec Canvas. Initialisez le modèle et attachez les médias dans l’ordre décrit sur cette page.