sources/webrtc-reviewed/src_front/src/webrtc/negotiate.ts

Текстовый файл из учебного комплекта, не свидетельство установленного runtime. Вернуться к учебнику · Исходный файл

   1 import type { AppConfig } from "../config/appConfig";
   2 
   3 export async function negotiate(
   4   pc: RTCPeerConnection,
   5   config: AppConfig,
   6   opts?: { sessionId?: string | null },
   7 ): Promise<string | null> {
   8   const offer = await pc.createOffer();
   9   await pc.setLocalDescription(offer);
  10 
  11   await waitIceGather(pc, config.webrtc.iceGatherTimeoutMs);
  12 
  13   const local = pc.localDescription;
  14   if (!local) throw new Error("localDescription is missing");
  15 
  16   const body: Record<string, unknown> = { sdp: local.sdp, type: local.type };
  17   const sessionId = opts?.sessionId;
  18   if (typeof sessionId === "string" && sessionId) body.session_id = sessionId;
  19 
  20   const resp = await fetch(config.signaling.offerEndpoint, {
  21     method: "POST",
  22     headers: { "Content-Type": "application/json" },
  23     body: JSON.stringify(body),
  24   });
  25 
  26   if (!resp.ok) throw new Error(`Signaling server responded with ${resp.status}`);
  27 
  28   const answer = (await resp.json()) as any;
  29   const sdp = typeof answer?.sdp === "string" ? answer.sdp : null;
  30   const type = typeof answer?.type === "string" ? answer.type : null;
  31   if (!sdp || !type) throw new Error("Signaling answer is missing sdp/type");
  32   await pc.setRemoteDescription({ sdp, type });
  33 
  34   return typeof answer?.session_id === "string" ? answer.session_id : null;
  35 }
  36 
  37 function waitIceGather(pc: RTCPeerConnection, timeoutMs: number): Promise<void> {
  38   if (pc.iceGatheringState === "complete") return Promise.resolve();
  39 
  40   return new Promise((resolve) => {
  41     const timer = window.setTimeout(() => {
  42       pc.removeEventListener("icegatheringstatechange", onChange);
  43       resolve();
  44     }, timeoutMs);
  45 
  46     const onChange = () => {
  47       if (pc.iceGatheringState === "complete") {
  48         window.clearTimeout(timer);
  49         pc.removeEventListener("icegatheringstatechange", onChange);
  50         resolve();
  51       }
  52     };
  53 
  54     pc.addEventListener("icegatheringstatechange", onChange);
  55   });
  56 }