Skip to main content

Qwen-LiveTranslate-Realtime Conexión mediante WebRTC

Qwen-LiveTranslate-Realtime Conexión mediante WebRTC

WebRTC usa pistas de medios para transmitir audio y DataChannel para transmitir eventos del modelo y texto. El cliente web puede usar la API nativa del navegador, mientras que los demás clientes pueden usar bibliotecas compatibles con WebRTC estándar. Model Studio no proporciona un SDK de WebRTC específico. Este documento proporciona el procedimiento de integración de WebRTC para este modelo y un ejemplo completo del lado del navegador.

Ámbito de aplicación

Este documento está dirigido a Qwen-LiveTranslate-Realtime. Las guías existentes enumeran qwen3.8-livetranslate-flash-realtime y qwen3.5-livetranslate-flash-realtime, que tienen configuraciones y campos de eventos diferentes; consulte Eventos del cliente. Los ejemplos de este documento usan qwen3.8-livetranslate-flash-realtime.
  • Use la API Key y el ID del espacio de trabajo correspondientes a la región y al espacio de trabajo seleccionados.
  • Dominio de Pekín: {WorkspaceId}.cn-beijing.maas.aliyuncs.com; dominio de Singapur: {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com.
  • Las API Keys de larga duración se almacenan en el servidor y no deben incluirse en aplicaciones móviles ni de navegador. Para obtener instrucciones sobre la generación de API Keys temporales y sus permisos, consulte Generar una API Key temporal.
Para capturar audio en el navegador, use un contexto seguro; para usar el micrófono, solicite los permisos correspondientes. Al recibir audio, maneje las restricciones de reproducción automática del navegador.

Autenticación e intercambio de SDP

El navegador envía una Offer SDP al AppServer de negocio. AppServer reenvía la siguiente solicitud con la API Key y devuelve la Answer SDP sin cambios al navegador; las API Keys de larga duración no llegan al frontend.
  • Método: POST
  • Dirección de Pekín: https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime
  • Dirección de Singapur: https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime
  • Encabezados de la solicitud: Authorization: Bearer <API_KEY>, Content-Type: application/sdp
  • Cuerpo de la solicitud: cadena Offer SDP.
  • Respuesta exitosa: HTTP 200, con la Answer SDP en el cuerpo; si se produce un error, se devuelve un código 4xx.
WebRTC realiza la autenticación durante el intercambio de SDP y no requiere un token de AOQ previo. En los ejemplos siguientes, /api/realtime/sdp hace referencia a una interfaz personalizada del AppServer de negocio, que debe implementar el proceso de reenvío descrito arriba; no es una URL proporcionada por Model Studio.

Flujo de trabajo

Secuencia de conexión WebRTC
  1. Prepare la clave de API y los parámetros del modelo objetivo en AppServer.
  2. Crear el objeto de conexión del cliente y registrar devoluciones de llamada.
  3. Configure las direcciones de medios y los DataChannels sin adjuntar pistas de medios ascendentes.
  4. Intercambie SDP a través de AppServer y espere a que la conexión y el canal de eventos estén listos.
  5. Configure el modelo según su protocolo. Adjunte las pistas de medios ascendentes necesarias después de la confirmación.
  6. Detenga la captura, cierre la conexión y libere los recursos cuando la interacción haya terminado.
  • El audio se transmite mediante las pistas de medios de WebRTC; la aplicación debe obtener la pista de audio local, vincular el reproductor remoto y enviar y recibir mensajes de DataChannel según las definiciones de eventos del modelo de destino.
  • La autenticación de WebRTC se realiza durante el intercambio de SDP; no se requiere ningún token AOQ. Utilice AppServer como proxy para la autenticación y el SDP a fin de mantener la clave API de largo plazo fuera del código frontend.
  • Una conexión exitosa indica que se estableció el canal de transporte, pero no significa que el modelo haya completado la inicialización. Al usar qwen3.8-livetranslate-flash-realtime en esta página, envíe session.update y active el flujo de medios ascendente necesario después de recibir session.updated.

Ejemplo completo

El siguiente es un ejemplo completo del lado del navegador. Primero implemente la API /api/realtime/sdp del AppServer de negocio según Autenticación e intercambio de SDP; después abra esta página HTML desde el mismo origen que el AppServer. El protocolo, el host y el puerto deben coincidir. Por ejemplo, la página y /api/realtime/sdp se sirven desde http://localhost:3000. El servidor almacena la API Key y reenvía el SDP; no coloque API Keys de larga duración en la página. Haga clic en “Finish input” para enviar session.finish, espere session.finished y, cuando termine de reproducirse el audio restante, haga clic en “Disconnect after audio playback”. Durante el establecimiento de la conexión o si necesita salir antes, haga clic en “Cancel and disconnect”; esta operación cierra la conexión de inmediato y puede provocar la pérdida de traducciones que aún no se hayan recibido o de audio que aún no se haya reproducido. Este ejemplo establece una conexión a la vez; vuelva a cargar la página para usarlo de nuevo.
<!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. Preparar autenticación y parámetros de conexión

AppServer configura el intercambio de SDP para el modelo, la región y el espacio de trabajo objetivo. Los siguientes parámetros se aplican a conexiones Realtime estándar. Para parámetros específicos de la aplicación, siga la mejor práctica correspondiente.
SettingValue
MétodoPOST
URLhttps://{endpoint}/api/v1/webrtc/realtime?model={model_name}
Content-Typeapplication/sdp
AuthorizationBearer <API_KEY>
Cuerpo de la solicitudOffer SDP
Respuesta exitosaAnswer SDP
Los detalles completos de región, dirección y autenticación se encuentran en Autenticación e intercambio de SDP. La interfaz del AppServer de negocio la implementa la propia empresa y no forma parte de la API del SDK de Model Studio.

2. Inicializar el objeto de conexión y registrar las funciones de callback

En el entorno web, cree una RTCPeerConnection; en otras plataformas, inicialice la biblioteca correspondiente. Antes de establecer la conexión, registre los siguientes controladores:
  • Cambios de estado de la conexión: determine si la conexión se estableció correctamente, falló o se desconectó.
  • Creación, apertura, mensajes y cierre de DataChannel: transporta los eventos del modelo.
  • Pista de medios remota: recibe y reproduce audio, o maneja otros medios admitidos por la aplicación de destino.
El ejemplo completo crea el objeto de conexión, las variables de estado y las funciones de callback en el mismo script. Cada llamada crea una sola conexión; al terminar, reinicialice estos objetos para realizar otra llamada. El objeto de conexión y las variables de estado se detallan en Ejemplo completo. El ejemplo completo usa finishRequested para evitar enviar repetidamente el evento de finalización. Registre los escuchadores de mensajes antes de inicializar el modelo para evitar perder los eventos de sesión que el servidor envía por iniciativa propia. No escuche únicamente los canales que crea el cliente: el servidor envía eventos mediante un canal llamado txt, por lo que debe manejar la función de callback datachannel.

3. Configurar direcciones de medios y DataChannels

Seleccionar la dirección de medios

Las direcciones son relativas al cliente. Los transceptores WebRTC negocian el envío y la recepción: sendrecv es bidireccional, sendonly solo envía y recvonly solo recibe. Las capacidades disponibles también dependen de la respuesta SDP del servidor y de la compatibilidad del modelo.
DirecciónConfiguración
Enviar audioNegocie el envío de audio y prepare el micrófono o una pista de audio externa. Agregue la pista cuando el modelo esté listo.
Recibir audioNegocie la recepción de audio y configure la reproducción en el callback de recepción de una pista remota.
Enviar videoNegocie el envío de video según la versión seleccionada y los escenarios admitidos, y prepare la cámara o una pista de video externa.
Por ejemplo, configure el audio ascendente cuando se ingrese voz y el audio descendente cuando se requiera traducción de voz. La Offer SDP debe incluir la sección de medios de audio m=audio; no elimine esta sección solo porque la aplicación no necesite reproducir audio.

Direcciones de medios para este modelo

Modelo o aplicaciónEnviar audioRecibir audioEnviar videoRecibir video
Qwen-LiveTranslate-Realtime Active cuando se use la entrada de voz Active cuando se requiera traducción de voz Configure de acuerdo con la versión y el escenario seleccionados No admite la activación
Este modelo genera texto o audio, pero no video. Esta tabla describe las direcciones de medios requeridas por la aplicación; no garantiza que cada combinación pueda negociarse de forma independiente. La configuración depende del modelo objetivo y de las capacidades de negociación del servidor.

Preparar medios sin enviarlos

Negocie las direcciones necesarias antes de crear la oferta. Para modelos que requieren confirmación de inicialización, reserve capacidades de envío sin adjuntar pistas capturadas. Una vez que el modelo esté listo, utilice replaceTrack en el remitente correspondiente para iniciar la transmisión. Registre previamente los controladores de recepción de medios. Configure el envío y la recepción de audio por separado según sea necesario; no combine la activación del micrófono, la reproducción de audio y la inicialización del modelo en una sola operación.

Ejemplo de configuración de medios

El siguiente ejemplo usa voz bidireccional. Las dos opciones en media controlan el envío y la recepción de audio, respectivamente. addTransceiver negocia la dirección de antemano; la pista capturada se almacena primero de forma local y solo se adjunta al emisor cuando el modelo está listo. Consulte el código en el ejemplo completo.

Crear canales de eventos

Cree un DataChannel antes de crear una Offer para que el SDP incluya la negociación del canal de datos. Consulte Envío y recepción de eventos del canal Data para conocer cómo el cliente y el servidor manejan la creación de canales. DataChannel se encarga de enviar los eventos de inicialización del modelo, texto y control, y de recibir los resultados, estados y errores del modelo. La pista de medios se encarga de transmitir el audio. El ejemplo completo crea un canal de eventos del cliente y registra la función de callback del canal del servidor antes de crear una Offer.

4. Establecer la conexión

Complete la conexión en el siguiente orden:
  1. Llame a createOffer y setLocalDescription.
  2. Recopile información de candidatos según los métodos ICE admitidos por el servidor; en este ejemplo, el SDP local se envía después de que termine la recopilación de ICE.
  3. Envíe el SDP local al AppServer de negocio, que usa la API Key para llamar a la interfaz de intercambio de SDP de Model Studio.
  4. Verifique el estado HTTP y, si la solicitud es exitosa, llame a setRemoteDescription con la Answer SDP devuelta.
  5. Espere a que el estado de la conexión indique éxito y confirme que el DataChannel que se usa para enviar eventos del modelo esté abierto.
Que el intercambio de SDP sea exitoso o que termine setRemoteDescription solo indica que se completó el paso de negociación correspondiente; no demuestra directamente que la conexión de medios se haya establecido. Tampoco se pueden enviar eventos del modelo cuando DataChannel no está en estado open.

Código de conexión y funciones de callback de estado

Escuche connectionstatechange para determinar el estado de la conexión y icegatheringstatechange para esperar el SDP local completo. La inicialización del modelo también requiere el estado open de DataChannel y el evento de disponibilidad del modelo; no debe basarse únicamente en la finalización del intercambio de SDP. El ejemplo completo realiza el intercambio de SDP mediante startSession() y maneja el resultado de la conexión mediante la función de callback del estado de la conexión. El AppServer de negocio debe reenviar la Offer con la dirección y los encabezados de solicitud de la sección 1; se espera que la API de ejemplo devuelva texto SDP sin formato. No pase directamente a setRemoteDescription contenido que incluya encabezados de respuesta HTTP, registros o contenedores JSON.

5. Intercambiar eventos del modelo y medios

Inicializar el modelo

Cuando la conexión y el canal de eventos estén disponibles, inicialice el modelo objetivo según su protocolo. Almacene los eventos de inicialización del servidor y combínelos con el estado del canal para avanzar en el flujo; no asuma un orden fijo de llegada de callbacks. Por ejemplo, al configurar un modelo con session.update, construya el evento según su protocolo y espere la confirmación de configuración antes de enviar la entrada. Los parámetros de voz, modalidades de salida, VAD y frecuencia de muestreo deben provenir de la documentación del modelo actual. session.created indica que se creó la sesión; no debe interpretarse siempre como que la configuración ya se aplicó. Para los modelos que requieren confirmación de la configuración de sesión, espere session.updated. Consulte Eventos del cliente y Eventos del servidor para conocer las definiciones de eventos del modelo. Maneje la transmisión de medios y los modos disponibles como se describe en esta página.

Ejemplos de eventos del modelo

El audio se transmite mediante la pista de medios y no usa el evento de envío de medios de WebSocket.
El siguiente ejemplo muestra el proceso session.created → session.update → session.updated para qwen3.8-livetranslate-flash-realtime. Configure los campos según la referencia de eventos del modelo. Adjunte la pista de medios ascendente solo después de recibir session.updated, que confirma que terminó la configuración. La siguiente configuración traduce voz al inglés con la voz Tina. Consulte el código en el ejemplo completo. Para el modelo de ejemplo qwen3.8-livetranslate-flash-realtime, cuando la salida de audio está activada, puede usar response.audio.done para verificar el estado de generación de audio del modelo; este evento no indica que el cliente haya terminado de reproducir el audio.

Iniciar transmisión y recepción de medios

Cuando el modelo esté listo, adjunte la pista de audio ascendente necesaria. Maneje la salida del modelo mediante la función de callback de la pista remota y analice continuamente los eventos que devuelve DataChannel.
  • El audio viaja a través de pistas de medios RTP; no se requiere el evento input_audio_buffer.append.
  • Los DataChannels transportan eventos de texto, estado, control y error.
En el ejemplo completo, startSendingMedia() adjunta la pista ascendente y ontrack procesa los medios recibidos.

6. Finalizar interacción y liberar recursos

Para el modelo de ejemplo qwen3.8-livetranslate-flash-realtime, al completar la entrada de audio, detenga la captura y transmisión de audio y envíe session.finish mediante DataChannel. Continúe recibiendo la última oración en su forma original, la traducción y el audio traducido, y espere session.finished. Este evento indica que el modelo terminó de procesar la traducción, pero no significa que el cliente haya terminado de reproducir el audio traducido restante; si se requiere la reproducción completa, llame a endSession() para liberar los recursos después de la reproducción. En el ejemplo completo, finishTranslation() usa la pista de audio y el DataChannel creados. Si la entrada proviene de un archivo o un búfer de negocio, envíe primero el audio almacenado en búfer antes de finalizar la entrada. La implementación de finishTranslation() se encuentra en Ejemplo completo. endSession() solo libera la conexión y los recursos de medios; no envía un evento de finalización. Después de completar la interacción con el modelo, la capa de negocio del cliente detiene la pista capturada localmente que mantiene la aplicación, cierra DataChannel y RTCPeerConnection, libera recursos como el reproductor y limpia el estado de negocio. Restablezca el estado de disponibilidad del modelo tras una desconexión fallida o inesperada. Inicialice el modelo nuevamente en una nueva conexión en lugar de reutilizar el estado de sesión de la conexión anterior. En el ejemplo completo, endSession() se encarga de liberar la conexión y los recursos de medios. Llame a endSession() después de completar la interacción con el modelo y procesar el audio traducido restante. Si la aplicación también usa grabación, debe detener MediaRecorder.

Enviar y recibir eventos mediante el canal Data

Verifique que el canal esté open antes de llamar a send con un evento serializado. Analice los mensajes entrantes en su callback message, incluidos los eventos en canales creados por el servidor. Los nombres, campos, parámetros y tiempos de envío de los eventos se ajustan a las definiciones de eventos del cliente del modelo de destino; el método de análisis, los cambios de estado, los resultados y el manejo de errores después de recibir mensajes se ajustan a las definiciones de eventos del servidor del modelo. Los eventos de inicialización se envían según los requisitos del modelo, y los eventos posteriores de texto y control se envían según las necesidades del negocio.

Ejemplo de DataChannel

Utilice la misma función de enlace para canales creados por el cliente y por el servidor. Esta gestiona el transporte y el análisis JSON; handleModelEvent en la sección 5 gestiona la semántica del modelo. Utilice sendModelEvent para la inicialización del modelo, texto o eventos de control únicamente cuando lo permita el protocolo del modelo. En el ejemplo completo, sendModelEvent() y bindDataChannel() envían y reciben eventos. En el ejemplo completo, se llama a startSession() al iniciar la traducción, a finishTranslation() al terminar la entrada y a endSession() después de finalizar la traducción, la reproducción y la limpieza. Estas funciones son ejemplos definidos en este documento y no son API integradas de WebRTC.

Definiciones de eventos de Qwen-LiveTranslate-Realtime

Modelo o aplicaciónEventos del cliente (envío)Eventos del servidor (recepción)
Qwen-LiveTranslate-RealtimeEventos del clienteEventos del servidor
Siga también estas reglas:
  • Use el campo type de los eventos para identificar los eventos del modelo y maneje los identificadores asociados según la referencia de eventos de este modelo.
  • Las operaciones que requiere el modelo, como la confirmación de inicialización, el envío de la entrada, la cancelación de respuestas y la finalización de tareas, se manejan en el canal Data de acuerdo con el protocolo correspondiente; constituyen la semántica de interacción del modelo y son independientes de la desconexión del transporte.
  • Los datos de audio se transmiten por las pistas de medios. Al usar la referencia de eventos del modelo, siga esta página para la encapsulación de medios y los modos de interacción disponibles. No aplique directamente el método de WebSocket para enviar medios.

Referencias de eventos