OpenAI WebRTC 与 WARP
当前Rust示例针对webrtc-sys 0.3.51:工厂启用SPED,连接配置启用SNAP。绑定库可能只暴露部分开关;在创建对等连接工厂之前初始化field trials,不将该片段误写为一次启用所有三项。 使用 WebRTC 简化往返协议(WARP)缩短 GPT-Live 或 Realtime API 语音会话启动时间。WARP 将 DTLS 1.3、SPED、SNAP 三种传输优化与预协商数据通道结合,以更少网络往返建立连接。
这些优化也可单独启用。即使不能使用完整 WARP,启用客户端支持的功能仍可降低延迟;全部结合才构成完整 WARP 握手。
WARP、SPED 与 SNAP 是 IETF Internet-Draft,规范与客户端支持仍可能变化。
GPT-Live 与 Realtime API 使用相同 WebRTC 优化,无论应用服务器用什么语言,都在浏览器或原生客户端配置。两种 API 的区别在于创建会话时如何发送协商好的数据通道 ID。
在原生客户端启用 WARP
完整 WARP 需要通过 field trials 启用传输优化,并预协商数据通道;两步也可独立使用。
启用 field trials
当前版本的 libwebrtc 包含 WARP 优化。原生客户端使用兼容构建时,启用以下试验开关:
来源示例 1(text):字段名、事件类型和示意值保留原样,含义见相邻正文;片段中的省略号不表示完整可运行程序。
WebRTC-ForceDtls13/Enabled/
WebRTC-Sctp-Snap/Enabled/
WebRTC-IceHandshakeDtls/Enabled/
三项分别启用 DTLS 1.3、SNAP 和 SPED,只包含想启用的优化即可。
某些集成要求合并成一条 field-trial 字符串:
来源示例 2(text):字段名、事件类型和示意值保留原样,含义见相邻正文;片段中的省略号不表示完整可运行程序。
WebRTC-ForceDtls13/Enabled/WebRTC-Sctp-Snap/Enabled/WebRTC-IceHandshakeDtls/Enabled/
应在创建对等连接工厂之前初始化开关。以下Rust绑定示例分别配置SPED与SNAP:
来源示例 3(rust):
// [中文] 按当前绑定库分别配置工厂与连接选项;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 时,通过 WebRTC 配置或初始化选项传递同一字符串。如果 SDK 未公开 field trials,应在 SDK 创建连接工厂前初始化底层 libwebrtc,或更新到提供该配置的封装。
预协商数据通道
客户端与信令请求使用相同通道 ID。以下步骤同时适用于原生客户端和浏览器,即使没有启用全部三种传输优化也可使用:
- 在 SDP offer 之前创建数据通道,将
negotiated设为true,id设为可用 ID,例如4;通道标签可为任意字符串。 - 把 SDP offer 和通道 ID 发给应用服务器,再按对应 API 执行下述步骤。
连接 GPT-Live
从 GPT-Live WebRTC 快速开始入手。服务器在 POST /v1/live/sessions JSON 请求体的 transport.dcid 包含通道 ID,transport.type 保持 webrtc,transport.sdp 是客户端 SDP offer。
对于 id: 4 的通道,向 POST /v1/live/sessions 发送以下 JSON,将 <SDP offer> 换成实际值:
来源示例 4(json):
程序文字译释114:这里替换为真实SDP offer。
{
"session": {
"model": "gpt-live-1"
},
"transport": {
"type": "webrtc",
"sdp": "<SDP offer>",
"dcid": 4
}
}
transport.dcid 启用预协商通道;标准数据通道则省略。不应把 dcid 放到 URL 查询参数:与下述 Realtime 流程不同,GPT-Live 会拒绝查询参数。
将返回的 transport.sdp 应用为远端 answer,等待 session.started 后再发应用命令。HTTP 请求已启动会话,不要在数据通道发送 session.start。
如果 SDK 未公开 transport.dcid,保留快速开始中的标准数据通道设置,仍可启用客户端支持的各项独立传输优化。
连接 Realtime API
使用统一 WebRTC 连接流程。服务器调用 /v1/realtime/calls 时,用 dcid 查询参数提供 ID。对于 id: 4,使用 POST /v1/realtime/calls?dcid=4,multipart 请求体保持相同 SDP 和会话配置。
将返回 SDP 应用为远端 answer。标准通道省略 dcid。下方完整示例展示两种配置。
在浏览器启用 WARP
Chrome、Edge 及其他 Chromium 浏览器内置 libwebrtc,但网页不能配置其 field trials。先启用浏览器可用的传输功能,再用与原生客户端相同的 API 专用信令预协商通道。
启用可用功能与 Origin Trial
Origin Trial让已注册网站启用试验性浏览器功能。逐项检查 WARP 功能可用性:
- DTLS 1.3:Chrome 无需 Origin Trial 即支持。
- SNAP:Chrome 151–156 通过 Origin Trial 支持。Edge 应确认当前版本是否提供 SNAP 试验,并注册 Edge 签发的令牌。
- SPED:尚无浏览器 Origin Trial。需后续关注支持情况。完整 WARP 要求浏览器默认启用 SPED,或由你控制启动参数并启用必要 field trials。
启用 SNAP 试验不等于启用 SPED 或完整 WARP。浏览器未公开 SPED 时,仍可通过 DTLS 1.3 与 SNAP 获得独立优化收益。
检查各浏览器试验:
启用 SNAP 的步骤:
- 打开浏览器 Origin Trials 页面,找到 WebRTC Data Channel: SCTP Negotiation Acceleration Protocol (SNAP)。
- 选择 Register,输入应用源,例如
https://example.com。 - 在页面
<head>中、创建RTCPeerConnection的脚本之前添加签发令牌:
来源示例 5(html):
<!-- [中文] 示例 5:启用可用功能与 Origin Trial;以下保留本语言原始示例。 -->
<!-- [中文] 将占位符替换为当前浏览器为应用源签发的 SNAP Origin Trial 令牌,并把 meta 放在页面 head 中、创建 RTCPeerConnection 的脚本之前。 -->
<!-- [中文] 此标签只启用令牌对应的浏览器试验,不能单独启用 SPED 或完整 WARP;不要保留占位符用于实际测试。 -->
<meta http-equiv="origin-trial" content="YOUR_ORIGIN_TRIAL_TOKEN" />
- 重新加载。Chrome 中打开 DevTools 的 Application,确认 Origin Trials 下出现
WebRtcSctpSnap。
也可通过 HTTP 响应头提供令牌:
来源示例 6(http):字段名、事件类型和示意值保留原样,含义见相邻正文;片段中的省略号不表示完整可运行程序。
Origin-Trial: YOUR_ORIGIN_TRIAL_TOKEN
令牌只对指定功能、签发浏览器及已注册源生效。SNAP 令牌不启用 SPED,Chrome 令牌也不在 Edge 启用试验。Firefox、Safari、iOS 浏览器和旧 Chromium 可支持标准 WebRTC,但不一定支持完整 WARP。
预协商浏览器数据通道
按上述数据通道设置,再依据 GPT-Live 或 Realtime API 的要求发送匹配 ID。预协商通道不需要 Origin Trial,即使没有完整 WARP,也可能缩短等待服务器事件的时间。
通过统一接口连接
使用统一 WebRTC 连接流程加快 Realtime API 连接。浏览器把 SDP offer 和已协商通道 ID 发给应用服务器,服务器调用 /v1/realtime/calls 时,将同一 ID 放在 dcid 查询参数中。
配置应用服务器
此服务器同时支持标准 WebRTC 与 WARP。标记 WARP only 的注释说明 WARP 所需的 dcid 转发:
来源示例 7(javascript):
程序文字译释115:生成令牌时出错。
程序文字译释116:生成令牌失败。
// [中文] 定义会话启动配置,区分Live语音模型与委派后端设置;配置对象本身不会建立连接、执行工具或自动运行后端。
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);
// [中文] 定义会话启动配置,区分Live语音模型与委派后端设置;配置对象本身不会建立连接、执行工具或自动运行后端。
// [中文] 本段为配置或调用片段,需结合外层程序与已建立连接使用;不作为独立完整程序宣称编译通过。
func handler(client openai.Client) http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("POST /session", func(w http.ResponseWriter, r *http.Request) {
offer, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 64*1024))
if err != nil || strings.TrimSpace(string(offer)) == "" {
http.Error(w, "A valid SDP offer is required", http.StatusBadRequest)
return
}
opts := []option.RequestOption{
option.WithHeader("OpenAI-Safety-Identifier", "hashed-user-id"),
}
// Forward the browser's negotiated data-channel ID when WARP is enabled.
if dcid := r.URL.Query().Get("dcid"); dcid != "" {
id, err := strconv.Atoi(dcid)
if err != nil || id < 0 || id > 65534 {
http.Error(w, "Invalid data-channel ID", 400)
return
}
opts = append(opts, option.WithQuery("dcid", dcid))
}
response, err := client.Realtime.Calls.New(r.Context(), realtime.CallNewParams{
Sdp: string(offer),
Session: realtime.RealtimeSessionCreateRequestParam{
Model: "gpt-realtime-2.1",
Audio: realtime.RealtimeAudioConfigParam{
Output: realtime.RealtimeAudioConfigOutputParam{
Voice: realtime.RealtimeAudioConfigOutputVoiceUnionParam{
OfString: openai.String("marin"),
},
},
},
},
}, opts...)
if err != nil {
http.Error(w, "Could not create Realtime call", http.StatusBadGateway)
return
}
defer response.Body.Close()
w.Header().Set("Content-Type", "application/sdp")
w.Header().Set("Location", response.Header.Get("Location"))
w.WriteHeader(response.StatusCode)
if _, err := io.Copy(w, response.Body); err != nil {
log.Print("Could not write SDP answer")
}
})
return mux
}
从浏览器连接
启用浏览器可用优化后,将 useWarp 设为 true。选择任一可用通道 ID,向服务器传递同一 ID。WARP only 标记的是标准 WebRTC 不需要的配置与信令:
来源示例 8(javascript):
// [中文] 示例 8:从浏览器连接;以下保留本语言原始示例。
// [中文] 沿原示例执行这些方法:pc.addTrack、pc.createDataChannel、pc.createOffer、pc.setLocalDescription、pc.setRemoteDescription;异步调用及事件接收顺序保留。
// 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,可在所用 WebRTC 栈实现兼容 WARP 的传输优化,或使用该栈支持的其他方式。相关规范包括 WARP、SPED、SNAP、DTLS 1.3 及数据通道建立。
无法使用这些优化时,采用标准 GPT-Live WebRTC 流程或 Realtime API 统一接口。

TRAE-AI编程


更多建议: