WebRTC Abridged Roundtrip Protocol(WARP) を使うと、GPT-Live または Realtime API の音声セッションが始まるまでの時間を短縮できます。WARP は、トランスポート層の 3 つの最適化(DTLS 1.3、SPED、SNAP)と事前にネゴシエーションしたデータチャネルを組み合わせ、接続の確立に必要な通信の往復回数を減らします。
これらの最適化は個別にも利用できます。WARP のすべての機能を利用できない場合でも、クライアントが対応する機能を有効にすれば、接続時間を短縮できます。すべての機能を組み合わせると、WARP の完全なハンドシェイクを利用できます。
WARP、SPED、SNAP は IETF のインターネットドラフトであり、仕様やクライアントの対応状況は変わる可能性があります。
GPT-Live と Realtime API は、同じ WebRTC 最適化を使用します。アプリケーションサーバーの実装言語にかかわらず、これらの最適化はブラウザまたはネイティブクライアントで設定します。セッション作成時にネゴシエーション済みのデータチャネル ID を送信する方法は、API によって異なります。
ネイティブクライアントでの WARP の有効化
WARP のすべての機能を使うには、フィールドトライアルでトランスポート層の最適化を有効にし、データチャネルを事前にネゴシエーションします。どちらか一方の手順だけを実施することもできます。
フィールドトライアルの有効化
現在の libwebrtc には WARP の最適化が含まれています。ネイティブクライアントでこれらの最適化に対応した libwebrtc ビルドを使用している場合は、次のフィールドトライアルを有効にします。
WebRTC-ForceDtls13/Enabled/
WebRTC-Sctp-Snap/Enabled/
WebRTC-IceHandshakeDtls/Enabled/
これらのトライアルは、上から順に DTLS 1.3、SNAP、SPED を有効にします。利用したい最適化に対応するトライアルだけを指定してください。
実装によっては、フィールドトライアルを 1 つの文字列にまとめて指定する必要があります。
WebRTC-ForceDtls13/Enabled/WebRTC-Sctp-Snap/Enabled/WebRTC-IceHandshakeDtls/Enabled/
ピア接続ファクトリを作成する前に、フィールドトライアルを初期化します。ラッパーによっては、設定できる項目が限られます。たとえば、webrtc-sys 0.3.51 では、SPED はピア接続ファクトリで有効にし、SNAP は接続設定で有効にします。
use webrtc_sys::peer_connection::ffi::{
ContinualGatheringPolicy, IceTransportsType, RtcConfiguration,
};
use webrtc_sys::peer_connection_factory::ffi::create_peer_connection_factory_with_options;
// The wrapper enables SPED on the factory; SNAP is an immutable connection option.
let factory = create_peer_connection_factory_with_options(false, true);
let configuration = RtcConfiguration {
ice_servers: vec![],
continual_gathering_policy: ContinualGatheringPolicy::GatherOnce,
ice_transport_type: IceTransportsType::All,
enable_sctp_snap: true,
};LiveKit のネイティブ SDK など、libwebrtc をラップする SDK を使用している場合は、SDK の WebRTC 設定または初期化オプションに同じフィールドトライアル文字列を渡します。SDK からフィールドトライアルを設定できない場合は、SDK がピア接続ファクトリを作成する前に内部の libwebrtc インスタンスを初期化するか、この設定に対応したラッパーに更新してください。
データチャネルの事前ネゴシエーション
クライアントとシグナリングリクエストでは、同じチャネル ID を使用します。次の手順はネイティブクライアントとブラウザの両方で使用できます。トランスポート層の最適化を 3 つすべて有効にしておく必要はありません。
- SDP オファーを作成する前にデータチャネルを作成します。作成時に
negotiatedをtrueに設定し、idには4などの未使用のチャネル ID を指定します。チャネルのラベルには任意の文字列を使用できます。 - SDP オファーとチャネル ID をアプリケーションサーバーに送信し、使用する API に応じて以下の手順に進みます。
GPT-Live への接続
まず、GPT-Live の WebRTC クイックスタートの手順に従います。サーバー側では、チャネル ID を transport.dcid として POST /v1/live/sessions の JSON ボディに含めます。transport.type には引き続き webrtc を指定し、transport.sdp にはクライアントの SDP オファーを指定します。
id: 4 で作成したチャネルの場合は、次の JSON ボディを POST /v1/live/sessions に送信します。<SDP offer> は、クライアントが生成した実際の SDP オファーに置き換えてください。
{
"session": {
"model": "gpt-live-1"
},
"transport": {
"type": "webrtc",
"sdp": "<SDP offer>",
"dcid": 4
}
}
transport.dcid フィールドを指定すると、事前にネゴシエーションしたチャネルが有効になります。標準のデータチャネルを使用する場合は省略してください。dcid は URL のクエリパラメーターとして送信しないでください。後述の Realtime API の接続手順とは異なり、GPT-Live はクエリパラメーターを拒否します。
返された transport.sdp をリモート側のアンサーとして適用し、session.started を受信してからアプリケーションのコマンドを送信します。セッションは HTTP リクエストで開始されるため、データチャネルで session.start を送信しないでください。
SDK から transport.dcid を指定できない場合は、GPT-Live クイックスタートにある標準のデータチャネル設定を使用してください。クライアントが対応するトランスポート層の最適化は、引き続き個別に有効にできます。
Realtime API への接続
統合インターフェースを使った WebRTC 接続手順に従います。サーバー側では、dcid クエリパラメーターにチャネル ID を指定して /v1/realtime/calls を呼び出します。id: 4 で作成したチャネルの場合は POST /v1/realtime/calls?dcid=4 を使用し、マルチパートのリクエストボディには同じ SDP とセッション設定を含めます。
返された SDP をリモート側のアンサーとして適用します。標準のデータチャネルを使用する場合は dcid を省略してください。後述の実装例では、両方の設定を示します。
ブラウザでの WARP の有効化
Chrome、Edge などの Chromium ベースのブラウザには libwebrtc が組み込まれていますが、ウェブページからフィールドトライアルを設定することはできません。ブラウザで利用できるトランスポート層の機能を有効にしてから、ネイティブクライアントと同じく、使用する API に応じたシグナリング手順でデータチャネルを事前にネゴシエーションします。
利用可能な機能とオリジントライアルの有効化
オリジントライアルを利用すると、登録したウェブサイトでブラウザの試験的な機能を有効にできます。WARP の各機能の対応状況を確認してください。
- DTLS 1.3: Chrome では、オリジントライアルに参加しなくても DTLS 1.3 を利用できます。
- SNAP: Chrome 151〜156 では、オリジントライアルを通じて SNAP を利用できます。Edge の場合は、使用中のバージョンで SNAP のオリジントライアルが利用できるか確認し、登録して Edge が発行するトークンを取得してください。
- SPED: 現時点では、SPED を利用できるブラウザのオリジントライアルはありません。今後の対応状況を改めて確認してください。WARP のすべての機能を使うには、ブラウザで SPED がデフォルトで有効になっている必要があります。デフォルトで有効になっていない場合は、ブラウザの起動フラグを自分で設定し、必要なフィールドトライアルを有効にする必要があります。
SNAP のオリジントライアルを有効にしただけでは、SPED は有効にならず、WARP の完全なハンドシェイクも利用できません。ブラウザで SPED を有効にできない場合でも、DTLS 1.3 と SNAP のオリジントライアルを利用すれば、それぞれの最適化の効果を得られます。
使用するブラウザのオリジントライアルを確認してください。
SNAP のオリジントライアルを有効にする手順は次のとおりです。
-
使用するブラウザのオリジントライアルのページを開き、WebRTC Data Channel: SCTP Negotiation Acceleration Protocol (SNAP) を探します。
-
Register を選択し、
https://example.comなど、アプリケーションのオリジンを入力します。 -
ページの
<head>内で、RTCPeerConnectionを作成するスクリプトより前に、発行されたトークンを追加します。<meta http-equiv="origin-trial" content="YOUR_ORIGIN_TRIAL_TOKEN" /> -
ページを再読み込みします。Chrome で DevTools を開き、アプリケーションを選択して、
WebRtcSctpSnapがオリジントライアルに表示されていることを確認します。
トークンは HTTP レスポンスヘッダーでも指定できます。
Origin-Trial: YOUR_ORIGIN_TRIAL_TOKEN
オリジントライアルのトークンは、指定された機能、トークンを発行したブラウザ、登録済みのオリジンの組み合わせでのみ有効です。SNAP 用のトークンでは SPED を有効にできず、Chrome が発行したトークンでは Edge のトライアルを有効にできません。Firefox、Safari、iOS 上のブラウザ、旧バージョンの Chromium は、WARP のすべての機能に対応していなくても、標準の WebRTC をサポートしている場合があります。
ブラウザのデータチャネルの事前ネゴシエーション
前述のデータチャネルの設定手順に従い、GPT-Live または Realtime API の手順で同じ ID を送信します。チャネルの事前ネゴシエーションにはオリジントライアルは不要です。WARP のすべての機能を利用できない場合でも、サーバーからのイベントを受信するまでの待ち時間を短縮できます。
統合インターフェースを使った接続
Realtime API への接続時間を短縮するには、統合インターフェースを使った WebRTC 接続手順に従います。ブラウザは、自身の SDP オファーとネゴシエーション済みのデータチャネル ID をアプリケーションサーバーに送信します。サーバーは、同じ ID を dcid クエリパラメーターに指定して /v1/realtime/calls を呼び出します。
アプリケーションサーバーの設定
この例のアプリケーションサーバーは、標準の WebRTC と WARP の両方に対応しています。WARP only というコメントは、WARP に必要な dcid の転送処理を示しています。
import express from "express";
const app = express();
// Parse raw SDP payloads posted from the browser
app.use(express.text({ type: ["application/sdp", "text/plain"] }));
const sessionConfig = JSON.stringify({
type: "realtime",
model: "gpt-realtime-2.1",
audio: { output: { voice: "marin" } },
});
// An endpoint which creates a Realtime API session.
app.post("/session", async (req, res) => {
const fd = new FormData();
fd.set("sdp", req.body);
fd.set("session", sessionConfig);
const endpoint = new URL("https://api.openai.com/v1/realtime/calls");
// WARP only: forward the negotiated data-channel ID to the Realtime API.
if (typeof req.query.dcid === "string") {
endpoint.searchParams.set("dcid", req.query.dcid);
}
try {
const r = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"OpenAI-Safety-Identifier": "hashed-user-id",
},
body: fd,
});
// Send back the SDP we received from the OpenAI REST API
const sdp = await r.text();
res.set(
"Content-Type",
r.ok ? "application/sdp" : (r.headers.get("Content-Type") ?? "text/plain")
);
const location = r.headers.get("Location");
if (location) res.set("Location", location);
res.status(r.status).send(sdp);
} catch (error) {
console.error("Session creation error:", error);
res.status(500).json({ error: "Failed to create session" });
}
});
app.listen(3000);ブラウザからの接続
ブラウザで利用できる最適化を有効にしてから、useWarp を true に設定します。未使用のデータチャネル ID を選び、同じ ID をサーバーに渡してください。WARP only というコメントは、標準の WebRTC では不要な設定とシグナリング処理を示しています。
// WARP only: set to true after enabling the supported WARP optimizations.
const useWarp = false;
// WARP only: choose any available data-channel ID.
const dataChannelId = 4;
// Create a peer connection
const pc = new RTCPeerConnection();
// Set up to play remote audio from the model
audioElement.current = document.createElement("audio");
audioElement.current.autoplay = true;
pc.ontrack = (e) => (audioElement.current.srcObject = e.streams[0]);
// Add local audio track for microphone input in the browser
const ms = await navigator.mediaDevices.getUserMedia({
audio: true,
});
pc.addTrack(ms.getTracks()[0]);
// Set up the event channel using any channel label.
// WARP only: pre-negotiate the channel using the selected data-channel ID.
const dc = pc.createDataChannel(
"events",
useWarp ? { negotiated: true, id: dataChannelId } : undefined,
);
// Start the session using the Session Description Protocol (SDP)
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const endpoint = new URL("/session", window.location.origin);
// WARP only: include the matching data-channel ID in the session request.
if (useWarp) {
endpoint.searchParams.set("dcid", String(dataChannelId));
}
const sdpResponse = await fetch(endpoint, {
method: "POST",
body: offer.sdp,
headers: {
"Content-Type": "application/sdp",
},
});
const answer = {
type: "answer",
sdp: await sdpResponse.text(),
};
await pc.setRemoteDescription(answer);WARP 非対応クライアントの利用
WebRTC クライアントが libwebrtc を使用していない場合は、WARP に対応するトランスポート層の最適化を WebRTC スタックに追加するか、そのスタックがサポートする別の方法を使用してください。関連する仕様は、WARP、SPED、SNAP、DTLS 1.3、データチャネルの確立です。
これらの最適化を利用できない場合は、標準の GPT-Live の WebRTC 接続手順または Realtime API の統合インターフェースを使用してください。