Skip to content
File

Blob: worker/src/client/radio/webrtc.ts

typescript65 lines
1import type { Answer } from '@/shared/contracts/signaling.ts';
2 
3export async function gatheredAnswer(peer: RTCPeerConnection): Promise<Answer> {
4 await peer.setLocalDescription(await peer.createAnswer());
5 if (peer.iceGatheringState !== 'complete')
6 await new Promise<void>((resolve, reject) => {
7 const timer = setTimeout(() => {
8 cleanup();
9 reject(new Error('Connection setup timed out. Try again.'));
10 }, 10000);
11 const changed = () => {
12 if (peer.iceGatheringState === 'complete') {
13 cleanup();
14 resolve();
15 }
16 };
17 const closed = () => {
18 if (peer.connectionState === 'closed') {
19 cleanup();
20 reject(new Error('Connection cancelled.'));
21 }
22 };
23 function cleanup() {
24 clearTimeout(timer);
25 peer.removeEventListener('icegatheringstatechange', changed);
26 peer.removeEventListener('connectionstatechange', closed);
27 }
28 peer.addEventListener('icegatheringstatechange', changed);
29 peer.addEventListener('connectionstatechange', closed);
30 changed();
31 closed();
32 });
33 if (!peer.localDescription?.sdp) throw new Error('Could not set up the connection. Try again.');
34 return { type: 'answer', sdp: peer.localDescription.sdp };
35}
36 
37export async function acknowledge(channel: RTCDataChannel): Promise<void> {
38 if (channel.readyState !== 'open')
39 await new Promise<void>((resolve, reject) => {
40 const timer = setTimeout(() => {
41 cleanup();
42 reject(new Error(`The ${channel.label} channel did not open. Try again.`));
43 }, 15000);
44 const opened = () => {
45 cleanup();
46 resolve();
47 };
48 const failed = () => {
49 cleanup();
50 reject(new Error('Data channel closed during setup. Try again.'));
51 };
52 function cleanup() {
53 clearTimeout(timer);
54 channel.removeEventListener('open', opened);
55 channel.removeEventListener('close', failed);
56 channel.removeEventListener('error', failed);
57 }
58 channel.addEventListener('open', opened);
59 channel.addEventListener('close', failed);
60 channel.addEventListener('error', failed);
61 if (channel.readyState === 'closed' || channel.readyState === 'closing') failed();
62 });
63 channel.send('ack');
64}