すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Model Studio:Qwen-LiveTranslate-Realtime WebRTC アクセス

最終更新日:Nov 06, 2026

Qwen-LiveTranslate-Realtime WebRTC アクセス

WebRTC はメディアトラックで音声を転送し、DataChannel でモデルイベントとテキストを転送します。Web クライアントはブラウザーのネイティブ API を使用でき、その他のクライアントは標準 WebRTC 対応ライブラリを使用できます。Model Studio は専用の WebRTC SDK を提供していません。

このドキュメントでは、このモデルの WebRTC アクセス手順と、ブラウザー側の完全なサンプルを示します。

適用範囲

このドキュメントは Qwen-LiveTranslate-Realtime を対象とします。既存のガイドには qwen3.8-livetranslate-flash-realtime と qwen3.5-livetranslate-flash-realtime が記載されており、設定とイベントフィールドが異なります。詳細は、クライアントイベントをご覧ください。このドキュメントの例は qwen3.8-livetranslate-flash-realtime を使用します。

  • 選択したリージョンとワークスペースに対応する API キーとワークスペース IDを使用してください。
  • 北京のドメイン:{WorkspaceId}.cn-beijing.maas.aliyuncs.com。シンガポールのドメイン:{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com。
  • 長期 API キーはサーバー側に保存し、ブラウザーやモバイルアプリケーションに含めないでください。一時 API キーの生成方法と権限は、一時 API キーの生成をご覧ください。

ブラウザーで音声をキャプチャする場合は、安全なコンテキストを使用してください。マイクを使用する場合は、マイクの権限を要求します。音声の受信時は、ブラウザーの自動再生制限に対応してください。

認証と SDP の交換

ブラウザーは業務 AppServer に Offer SDP を送信します。AppServer は API キーを使用して次のリクエストを転送し、Answer SDP をそのままブラウザーに返します。長期 API キーはフロントエンドに渡りません。

  • メソッド:POST
  • 北京のアドレス:https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime
  • シンガポールのアドレス:https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime
  • リクエストヘッダー:Authorization: Bearer <API_KEY>、Content-Type: application/sdp
  • リクエストボディ:Offer SDP の文字列。
  • 成功時のレスポンス:HTTP 200。ボディに Answer SDP が含まれます。失敗時は 4xx を返します。

WebRTC は SDP の交換時に認証を行うため、事前に AOQ Token を取得する必要はありません。以下のサンプルの /api/realtime/sdp は、業務 AppServer が構築するカスタムインターフェイスです。前述の転送処理を実装する必要があり、Model Studio が提供する URL ではありません。

処理フロー

WEBRTC 接続シーケンス図
  1. AppServer に API キーと対象モデルのパラメーターを用意します。
  2. クライアントの接続オブジェクトを作成し、コールバックを登録します。
  3. 上りメディアトラックを接続せずに、メディア方向と DataChannel を設定します。
  4. AppServer 経由で SDP を交換し、接続とイベントチャネルの準備完了を待ちます。
  5. モデルのプロトコルに従って設定します。確認後、必要な上りメディアトラックを接続します。
  6. 対話終了時にキャプチャを停止し、接続を閉じてリソースを解放します。

注記

  • 音声は WebRTC のメディアトラックで転送します。アプリケーションは、ローカル音声トラックの取得、リモートプレーヤーのバインド、対象モデルのイベント定義に従った DataChannel メッセージの送受信を行います。

  • WebRTC の認証は SDP 交換時に行うため、AOQ トークンの取得は不要です。長期 API キーをフロントエンドに置かないよう、AppServer 経由で認証と SDP を中継します。

  • 接続成功は転送チャネルの確立を示し、モデルの初期化完了を意味しません。このページの qwen3.8-livetranslate-flash-realtime を使用する場合は、session.update を送信し、session.updated を受信してから、必要な上りメディアストリームを有効にします。

完全なサンプル

以下はブラウザー側の完全なサンプルです。まず、認証と SDP の交換に従って業務 AppServer の /api/realtime/sdp API を実装し、業務 AppServer と同じオリジンでこの HTML ページを開きます。プロトコル、ホスト、ポートを一致させてください。たとえば、ページと /api/realtime/sdp の両方を http://localhost:3000 から配信します。API キーの保存と SDP の転送はサーバーが行い、長期 API キーをページに含めないでください。「Finish input」で session.finish を送信し、session.finished を待ちます。残りの音声の再生後に「Disconnect after audio playback」をクリックします。接続処理中や途中で終了する場合は「Cancel and disconnect」をクリックします。この操作は直ちに接続を閉じるため、未受信の翻訳や未再生の音声が失われる場合があります。このサンプルは一度に 1 つの接続を確立します。再利用するには、ページを再読み込みしてください。

<!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. 認証と接続パラメーターの準備

AppServer で対象モデル、リージョン、ワークスペースに応じた SDP 交換を設定します。以下は標準 Realtime 接続のパラメーターです。アプリケーション固有のパラメーターは、対応するベストプラクティスに従ってください。

設定項目値
メソッドPOST
URLhttps://{endpoint}/api/v1/webrtc/realtime?model={model_name}
Content-Typeapplication/sdp
AuthorizationBearer <API_KEY>
リクエストボディOffer SDP
成功時のレスポンスAnswer SDP

リージョン、アドレス、認証の詳細は、認証と SDP の交換をご覧ください。業務 AppServer のインターフェイスは業務側で実装するもので、Model Studio の SDK API には含まれません。

2. 接続オブジェクトの初期化とコールバックの登録

Web 側では RTCPeerConnection を作成します。その他のプラットフォームでは、対応するライブラリを初期化します。接続前に、次のハンドラーを登録してください。

  • 接続状態の変化:接続の成功、失敗、切断を判定します。

  • DataChannel の確立、オープン、メッセージ、クローズ:モデルイベントを扱います。

  • リモートメディアトラック:音声の受信と再生、または対象アプリケーションがサポートするその他のメディアの処理を行います。

完全なサンプルでは、同じスクリプト内で接続オブジェクト、状態変数、コールバックを作成します。1 回の呼び出しにつき 1 つの接続を作成し、終了後に再度呼び出す場合は、これらのオブジェクトを再初期化してください。

接続オブジェクトと状態変数の詳細は、完全なサンプルをご覧ください。

完全なサンプルでは、finishRequested で終了イベントの重複送信を防ぎます。

サーバーが自発的に送信するセッションイベントを取りこぼさないよう、モデル初期化前にメッセージリスナーを登録してください。クライアントが作成したチャネルだけを監視しないでください。サーバーは txt という名前のチャネルでイベントを送信するため、datachannel コールバックを処理する必要があります。

3. メディア方向と DataChannel の設定

メディア方向の選択

方向はクライアントから見たものです。WebRTC トランシーバーで送受信をネゴシエートします。sendrecv は双方向、sendonly は送信のみ、recvonly は受信のみです。利用可能な機能はサーバーの SDP 応答とモデルの対応状況にも依存します。

方向設定
音声を送信音声送信機能をネゴシエートし、マイクまたは外部音声トラックを準備します。モデルの準備が整ってからトラックを接続します。
音声を受信音声受信機能をネゴシエートし、リモートトラックのコールバックで音声再生をバインド
動画を送信選択したバージョンと対応シナリオに基づき動画送信機能をネゴシエートし、カメラまたは外部動画トラックを準備

たとえば、発話を入力する場合は上り音声を、音声翻訳が必要な場合は下り音声を設定します。Offer SDP には音声メディアセクション m=audio が必要です。アプリケーションで音声再生が不要であっても、このセクションを削除しないでください。

このモデルのメディア方向

モデルまたはアプリケーション音声送信音声受信映像送信映像受信
Qwen-LiveTranslate-Realtime 音声入力を使用する場合に有効化音声翻訳が必要な場合に有効化選択したバージョンとシナリオに従って設定有効化非対応

このモデルはテキストまたは音声を出力し、動画は出力しません。

この表はアプリケーションに必要なメディア方向を示すもので、すべての組み合わせを個別にネゴシエートできることを保証するものではありません。実際の設定は対象モデルとサーバーのネゴシエーション機能に依存します。

送信せずにメディアを準備

Offer 作成前に必要な方向をネゴシエートします。初期化確認が必要なモデルでは、取得したトラックを接続せずに送信機能を確保します。モデルの準備完了後、対応する sender の replaceTrack で送信を開始します。メディア受信ハンドラーは事前に登録してください。

音声の送信と受信は、必要に応じて個別に設定します。マイクの有効化、音声の再生、モデルの初期化を 1 つの操作にまとめないでください。

メディア設定の例

以下は双方向音声の例です。media の 2 つのスイッチが、それぞれ音声の送信と受信を制御します。addTransceiver で方向を事前にネゴシエートし、キャプチャしたトラックはローカルに保存して、モデルの準備が整ってから送信側に接続します。

コードは、完全なサンプルをご覧ください。

イベントチャネルの作成

Offer の作成前に DataChannel を作成して、SDP にデータチャネルのネゴシエーションを含めます。クライアントとサーバーがチャネルの作成を処理する方法は、データチャネルでのイベント送受信をご覧ください。

DataChannel は、モデル初期化、テキスト、制御イベントの送信と、モデルの結果、状態、エラーの受信を担います。メディアトラックは音声の転送を担います。

完全なサンプルでは、Offer の作成前にクライアントイベントチャネルを作成し、サーバー側のチャネルコールバックを登録します。

4. 接続の確立

次の順序で接続を完了します。

  1. createOffer と setLocalDescription を呼び出します。
  2. サーバーがサポートする ICE 方式に従って候補情報を収集します。このサンプルでは、ICE の収集が完了してからローカル SDP を送信します。
  3. ローカル SDP を業務 AppServer に送信します。AppServer は API キーを使用して Model Studio の SDP 交換インターフェイスを呼び出します。
  4. HTTP ステータスを確認し、成功時は返された Answer SDP を指定して setRemoteDescription を呼び出します。
  5. 接続状態が成功に変わるのを待ち、モデルイベントの送信に使用する DataChannel がオープンしていることを確認します。

SDP 交換の成功や setRemoteDescription の完了は、対応するネゴシエーション手順の完了を示すだけで、メディア接続の成功を直接示すものではありません。また、DataChannel が open 状態でない場合は、モデルイベントを送信できません。

接続コードと状態コールバック

connectionstatechange で接続状態を判定し、icegatheringstatechange でローカル SDP の完成を待ちます。モデルの初期化には DataChannel の open とモデルの準備完了イベントも必要です。SDP 交換の完了だけで判断しないでください。

完全なサンプルでは、startSession() で SDP 交換を完了し、接続状態コールバックで接続結果を処理します。

業務 AppServer は、セクション 1 のアドレスとリクエストヘッダーに従って Offer を転送します。サンプル API はプレーンな SDP テキストを返す想定です。HTTP レスポンスヘッダー、ログ、JSON ラッパーを含む内容を、そのまま setRemoteDescription に渡さないでください。

5. モデルイベントとメディアの交換

モデルの初期化

接続とイベントチャネルが利用可能になったら、対象モデルのプロトコルに従って初期化します。サーバーの初期化イベントを保持し、チャネル状態と組み合わせて処理を進めます。コールバックの到着順序が固定されているとは想定しないでください。

たとえば、session.update でモデルを設定する場合は、そのプロトコルに従ってイベントを構築し、設定の確認を待ってから入力を送信します。音声、出力モダリティ、VAD、サンプリングレートなどのパラメーターは、現在のモデルのドキュメントに従ってください。

session.created はセッションの作成を示し、設定の適用完了を一律に示すものではありません。セッション設定の確認が必要なモデルでは、session.updated を待ってください。

モデルのイベント定義は、クライアントイベントとサーバーイベントをご覧ください。メディアの転送と利用可能なモードは、このページの説明に従ってください。

モデルイベントの例

注記音声はメディアトラックで転送し、WebSocket のメディア送信イベントは使用しません。

次の例は、qwen3.8-livetranslate-flash-realtime の session.created → session.update → session.updated フローを示します。モデルのイベントリファレンスに従ってフィールドを設定します。設定完了を確認する session.updated を受信してから、上りメディアトラックを接続してください。

次の設定は、Tina の音声を使用して発話を英語に翻訳します。

コードは、完全なサンプルをご覧ください。

サンプルモデル qwen3.8-livetranslate-flash-realtime では、音声出力を有効にすると response.audio.done でモデルの音声生成状態を確認できます。このイベントは、クライアントの音声再生の完了を示しません。

メディア送受信の開始

モデルの準備が整ったら、必要な上り音声トラックを接続します。リモートトラックのコールバックでモデル出力を処理し、DataChannel から返されるイベントを継続的に解析します。

  • 音声は RTP メディアトラックで転送するため、input_audio_buffer.append は不要です。

  • DataChannel はテキスト、状態、制御、エラーイベントを転送します。

完全なサンプルでは、startSendingMedia() が上りトラックを接続し、ontrack が受信メディアを処理します。

6. 対話の終了とリソースの解放

サンプルモデル qwen3.8-livetranslate-flash-realtime では、音声入力の終了後、音声のキャプチャと送信を停止し、DataChannel で session.finish を送信します。最後の文の原文、訳文、翻訳音声を引き続き受信し、session.finished を待ちます。このイベントはモデルの翻訳処理完了を示し、クライアントの残りの翻訳音声の再生完了は意味しません。最後まで再生する必要がある場合は、再生完了後に endSession() を呼び出してリソースを解放します。

完全なサンプルでは、finishTranslation() は作成済みの音声トラックと DataChannel を使用します。ファイルや業務バッファーから入力する場合は、バッファー内の音声を送信してから入力を終了する必要があります。

finishTranslation() の実装は、完全なサンプルをご覧ください。

endSession() は接続とメディアリソースを解放するだけで、終了イベントを送信しません。

モデルとの対話が完了したら、クライアントの業務レイヤーは、アプリケーションが保持するローカルキャプチャトラックを停止し、DataChannel と RTCPeerConnection を閉じ、プレーヤーなどのリソースを解放して業務状態を消去します。

接続失敗や予期しない切断後はモデルの準備完了状態をクリアします。新しい接続では以前のセッション状態を再利用せず、モデルを再初期化してください。

完全なサンプルでは、endSession() が接続とメディアリソースを解放します。

モデルとの対話を完了し、残りの翻訳音声を処理した後に endSession() を呼び出します。業務で別途録音も行う場合は、MediaRecorder も停止する必要があります。

データチャネルでのイベント送受信

シリアライズしたイベントを send で送信する前に、チャネルが open であることを確認します。サーバーが作成したチャネルのイベントも含め、受信メッセージを message コールバックで解析します。

送信するイベントの名前、フィールド、パラメーター、タイミングは、対象モデルのクライアントイベント定義に従います。受信後の解析、状態変化、結果、エラー処理は、モデルのサーバーイベント定義に従います。初期化イベントはモデルの要件に応じて送信し、その後のテキストイベントや制御イベントはアプリケーションの必要に応じて送信してください。

DataChannel の例

クライアントが作成したチャネルとサーバーが作成したチャネルに同じバインド関数を使用します。この関数は転送と JSON 解析を担当し、セクション 5 の handleModelEvent がモデルの意味に応じた処理を行います。sendModelEvent は、モデルのプロトコルで許可される場合にのみ初期化、テキスト、制御イベントに使用します。

完全なサンプルでは、sendModelEvent() と bindDataChannel() でイベントを送受信します。

完全なサンプルでは、翻訳開始時に startSession()、入力終了時に finishTranslation()、翻訳と再生の後処理完了後に endSession() を呼び出します。これらはこのドキュメントのサンプル関数で、WebRTC の組み込み API ではありません。

Qwen-LiveTranslate-Realtime のイベント定義

モデルまたはアプリケーションクライアントイベント(送信)サーバーイベント(受信)
Qwen-LiveTranslate-Realtimeクライアントイベントサーバーイベント

次のルールにも従ってください。

  • イベントの type フィールドでモデルイベントを識別し、関連する識別子はこのモデルのイベントリファレンスに従って処理します。

  • モデルが必要とする初期化の確認、入力のコミット、レスポンスのキャンセル、タスクの完了などの操作は、対応するプロトコルに従って Data チャネル内で処理します。これらはモデルの対話動作であり、トランスポート接続の切断とは別の操作です。

  • 音声データはメディアトラックで転送します。モデルのイベントリファレンスを使用する場合、メディアのカプセル化と使用可能な対話モードはこのページに従ってください。WebSocket のメディア送信方法をそのまま適用しないでください。

イベントリファレンス