@gjsify/webrtc 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +73 -70
- package/src/get-user-media.ts +0 -131
- package/src/gst-enum-maps.ts +0 -125
- package/src/gst-init.ts +0 -49
- package/src/gst-stats-parser.ts +0 -137
- package/src/gst-utils.ts +0 -41
- package/src/index.ts +0 -104
- package/src/internal/gst-types.ts +0 -122
- package/src/media-device-info.ts +0 -33
- package/src/media-devices.ts +0 -191
- package/src/media-stream-track.ts +0 -159
- package/src/media-stream.ts +0 -96
- package/src/register/data-channel.ts +0 -11
- package/src/register/error.ts +0 -11
- package/src/register/media-devices.ts +0 -10
- package/src/register/media.ts +0 -15
- package/src/register/peer-connection.ts +0 -20
- package/src/register.spec.ts +0 -55
- package/src/register.ts +0 -10
- package/src/rtc-certificate.ts +0 -110
- package/src/rtc-data-channel.ts +0 -283
- package/src/rtc-dtls-transport.ts +0 -48
- package/src/rtc-dtmf-sender.ts +0 -146
- package/src/rtc-error.ts +0 -49
- package/src/rtc-events.ts +0 -64
- package/src/rtc-ice-candidate.ts +0 -115
- package/src/rtc-ice-transport.ts +0 -104
- package/src/rtc-peer-connection.ts +0 -1039
- package/src/rtc-rtp-receiver.ts +0 -122
- package/src/rtc-rtp-sender.ts +0 -471
- package/src/rtc-rtp-transceiver.ts +0 -131
- package/src/rtc-sctp-transport.ts +0 -48
- package/src/rtc-session-description.ts +0 -64
- package/src/rtc-stats-report.ts +0 -39
- package/src/rtc-track-event.ts +0 -45
- package/src/rtp-capabilities.ts +0 -48
- package/src/tee-multiplexer.ts +0 -75
- package/src/test.mts +0 -11
- package/src/webrtc.spec.ts +0 -1186
- package/src/wpt-helpers.ts +0 -156
- package/src/wpt-media.spec.ts +0 -1154
- package/src/wpt.spec.ts +0 -1136
- package/tsconfig.json +0 -36
- package/tsconfig.tsbuildinfo +0 -1
package/src/rtc-dtmf-sender.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// W3C RTCDTMFSender for GJS.
|
|
2
|
-
//
|
|
3
|
-
// Implements tone insertion, validation, and tonechange event dispatch.
|
|
4
|
-
// Actual DTMF RTP packet generation requires GStreamer dtmfsrc integration
|
|
5
|
-
// (Phase 4.6+); the timer-based event loop is spec-compliant regardless.
|
|
6
|
-
//
|
|
7
|
-
// Reference: W3C WebRTC spec § 7
|
|
8
|
-
// Reference: refs/wpt/webrtc/RTCDTMFSender-insertDTMF.https.html
|
|
9
|
-
// Reference: refs/wpt/webrtc/RTCDTMFSender-helper.js
|
|
10
|
-
|
|
11
|
-
import '@gjsify/dom-events/register/event-target';
|
|
12
|
-
|
|
13
|
-
const VALID_DTMF_CHARS = new Set('0123456789ABCDabcd#*,');
|
|
14
|
-
const MIN_DURATION = 40;
|
|
15
|
-
const MAX_DURATION = 6000;
|
|
16
|
-
const DEFAULT_DURATION = 100;
|
|
17
|
-
const MIN_INTER_TONE_GAP = 30;
|
|
18
|
-
const DEFAULT_INTER_TONE_GAP = 70;
|
|
19
|
-
const COMMA_DELAY = 2000;
|
|
20
|
-
|
|
21
|
-
type EventHandler = ((ev: Event) => void) | null;
|
|
22
|
-
|
|
23
|
-
export interface RTCDTMFToneChangeEventInit extends EventInit {
|
|
24
|
-
tone?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class RTCDTMFToneChangeEvent extends Event {
|
|
28
|
-
readonly tone: string;
|
|
29
|
-
|
|
30
|
-
constructor(type: string, init: RTCDTMFToneChangeEventInit = {}) {
|
|
31
|
-
super(type, init);
|
|
32
|
-
this.tone = init.tone ?? '';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class RTCDTMFSender extends EventTarget {
|
|
37
|
-
private _toneBuffer = '';
|
|
38
|
-
private _duration = DEFAULT_DURATION;
|
|
39
|
-
private _interToneGap = DEFAULT_INTER_TONE_GAP;
|
|
40
|
-
private _timerId: ReturnType<typeof setTimeout> | null = null;
|
|
41
|
-
private _canInsert = true;
|
|
42
|
-
private _ontonechange: EventHandler = null;
|
|
43
|
-
|
|
44
|
-
/** @internal — back-references set by RTCRtpSender */
|
|
45
|
-
_isStopped: () => boolean = () => false;
|
|
46
|
-
_getCurrentDirection: () => string | null = () => null;
|
|
47
|
-
|
|
48
|
-
get toneBuffer(): string { return this._toneBuffer; }
|
|
49
|
-
get canInsertDTMF(): boolean { return this._canInsert; }
|
|
50
|
-
|
|
51
|
-
get ontonechange(): EventHandler { return this._ontonechange; }
|
|
52
|
-
set ontonechange(v: EventHandler) { this._ontonechange = v; }
|
|
53
|
-
|
|
54
|
-
insertDTMF(tones: string, duration?: number, interToneGap?: number): void {
|
|
55
|
-
// Step 3: If transceiver.stopped is true, throw InvalidStateError
|
|
56
|
-
if (this._isStopped()) {
|
|
57
|
-
throw new DOMException(
|
|
58
|
-
"Failed to execute 'insertDTMF': The associated transceiver is stopped",
|
|
59
|
-
'InvalidStateError',
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Step 4: If currentDirection is recvonly or inactive, throw InvalidStateError
|
|
64
|
-
const dir = this._getCurrentDirection();
|
|
65
|
-
if (dir === 'recvonly' || dir === 'inactive') {
|
|
66
|
-
throw new DOMException(
|
|
67
|
-
"Failed to execute 'insertDTMF': The associated transceiver direction does not allow sending",
|
|
68
|
-
'InvalidStateError',
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Step 6: If tones contains invalid characters, throw InvalidCharacterError
|
|
73
|
-
for (const ch of tones) {
|
|
74
|
-
if (!VALID_DTMF_CHARS.has(ch)) {
|
|
75
|
-
throw new DOMException(
|
|
76
|
-
`Failed to execute 'insertDTMF': Invalid DTMF character '${ch}'`,
|
|
77
|
-
'InvalidCharacterError',
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Normalize a-d to uppercase
|
|
83
|
-
tones = tones.replace(/[a-d]/g, c => c.toUpperCase());
|
|
84
|
-
|
|
85
|
-
// Clamp duration to [40, 6000]
|
|
86
|
-
const d = duration ?? DEFAULT_DURATION;
|
|
87
|
-
this._duration = Math.max(MIN_DURATION, Math.min(MAX_DURATION, d));
|
|
88
|
-
|
|
89
|
-
// Clamp interToneGap to [30, ...]
|
|
90
|
-
const g = interToneGap ?? DEFAULT_INTER_TONE_GAP;
|
|
91
|
-
this._interToneGap = Math.max(MIN_INTER_TONE_GAP, g);
|
|
92
|
-
|
|
93
|
-
// Step 9: Set the object's toneBuffer to tones
|
|
94
|
-
this._toneBuffer = tones;
|
|
95
|
-
|
|
96
|
-
// Step 10: If toneBuffer was empty before, start the playout task
|
|
97
|
-
if (this._timerId !== null) {
|
|
98
|
-
clearTimeout(this._timerId);
|
|
99
|
-
this._timerId = null;
|
|
100
|
-
}
|
|
101
|
-
if (tones.length > 0) {
|
|
102
|
-
this._scheduleNextTone(0);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private _scheduleNextTone(delay: number): void {
|
|
107
|
-
this._timerId = setTimeout(() => {
|
|
108
|
-
this._timerId = null;
|
|
109
|
-
this._playNextTone();
|
|
110
|
-
}, delay);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private _playNextTone(): void {
|
|
114
|
-
if (this._toneBuffer.length === 0) {
|
|
115
|
-
// Spec: fire one final tonechange with empty tone
|
|
116
|
-
this._fireToneChange('');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const tone = this._toneBuffer[0];
|
|
121
|
-
this._toneBuffer = this._toneBuffer.slice(1);
|
|
122
|
-
|
|
123
|
-
// Fire tonechange event for this tone
|
|
124
|
-
this._fireToneChange(tone);
|
|
125
|
-
|
|
126
|
-
// Schedule next tone after duration + interToneGap (comma = 2s delay)
|
|
127
|
-
const delay = tone === ',' ? COMMA_DELAY : (this._duration + this._interToneGap);
|
|
128
|
-
this._scheduleNextTone(delay);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private _fireToneChange(tone: string): void {
|
|
132
|
-
const ev = new RTCDTMFToneChangeEvent('tonechange', { tone });
|
|
133
|
-
this._ontonechange?.call(this, ev);
|
|
134
|
-
this.dispatchEvent(ev);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** @internal — called by RTCRtpSender on cleanup */
|
|
138
|
-
_stop(): void {
|
|
139
|
-
if (this._timerId !== null) {
|
|
140
|
-
clearTimeout(this._timerId);
|
|
141
|
-
this._timerId = null;
|
|
142
|
-
}
|
|
143
|
-
this._toneBuffer = '';
|
|
144
|
-
this._canInsert = false;
|
|
145
|
-
}
|
|
146
|
-
}
|
package/src/rtc-error.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// RTCError extends DOMException per W3C WebRTC spec §7.
|
|
2
|
-
//
|
|
3
|
-
// Reference: refs/node-datachannel/src/polyfill/RTCError.ts (MIT) +
|
|
4
|
-
// https://www.w3.org/TR/webrtc/#rtcerror-interface
|
|
5
|
-
//
|
|
6
|
-
// CLAUDE.md Rule 8 exception: the class below extends `DOMException`
|
|
7
|
-
// (a global). Seed the global before the class body is evaluated.
|
|
8
|
-
|
|
9
|
-
import '@gjsify/dom-exception/register';
|
|
10
|
-
|
|
11
|
-
export type RTCErrorDetailType =
|
|
12
|
-
| 'data-channel-failure'
|
|
13
|
-
| 'dtls-failure'
|
|
14
|
-
| 'fingerprint-failure'
|
|
15
|
-
| 'sctp-failure'
|
|
16
|
-
| 'sdp-syntax-error'
|
|
17
|
-
| 'hardware-encoder-not-available'
|
|
18
|
-
| 'hardware-encoder-error';
|
|
19
|
-
|
|
20
|
-
export interface RTCErrorInit {
|
|
21
|
-
errorDetail: RTCErrorDetailType;
|
|
22
|
-
sdpLineNumber?: number | null;
|
|
23
|
-
sctpCauseCode?: number | null;
|
|
24
|
-
receivedAlert?: number | null;
|
|
25
|
-
sentAlert?: number | null;
|
|
26
|
-
httpRequestStatusCode?: number | null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class RTCError extends DOMException {
|
|
30
|
-
readonly errorDetail: RTCErrorDetailType;
|
|
31
|
-
readonly sdpLineNumber: number | null;
|
|
32
|
-
readonly sctpCauseCode: number | null;
|
|
33
|
-
readonly receivedAlert: number | null;
|
|
34
|
-
readonly sentAlert: number | null;
|
|
35
|
-
readonly httpRequestStatusCode: number | null;
|
|
36
|
-
|
|
37
|
-
constructor(init: RTCErrorInit, message?: string) {
|
|
38
|
-
super(message ?? init.errorDetail, 'OperationError');
|
|
39
|
-
if (!init || !init.errorDetail) {
|
|
40
|
-
throw new TypeError('RTCError: errorDetail is required');
|
|
41
|
-
}
|
|
42
|
-
this.errorDetail = init.errorDetail;
|
|
43
|
-
this.sdpLineNumber = init.sdpLineNumber ?? null;
|
|
44
|
-
this.sctpCauseCode = init.sctpCauseCode ?? null;
|
|
45
|
-
this.receivedAlert = init.receivedAlert ?? null;
|
|
46
|
-
this.sentAlert = init.sentAlert ?? null;
|
|
47
|
-
this.httpRequestStatusCode = init.httpRequestStatusCode ?? null;
|
|
48
|
-
}
|
|
49
|
-
}
|
package/src/rtc-events.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// W3C WebRTC event classes.
|
|
2
|
-
//
|
|
3
|
-
// Reference: refs/node-gst-webrtc/src/webrtc/events.ts (ISC) +
|
|
4
|
-
// https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent-interface
|
|
5
|
-
// https://www.w3.org/TR/webrtc/#rtcdatachannelevent-interface
|
|
6
|
-
// https://www.w3.org/TR/webrtc/#rtcerrorevent-interface
|
|
7
|
-
//
|
|
8
|
-
// CLAUDE.md Rule 8 exception: the class declarations below extend the
|
|
9
|
-
// global `Event` constructor, which runs at module load. Seed the global
|
|
10
|
-
// before the class bodies are evaluated.
|
|
11
|
-
|
|
12
|
-
import '@gjsify/dom-events/register/event-target';
|
|
13
|
-
|
|
14
|
-
import type { RTCIceCandidate } from './rtc-ice-candidate.js';
|
|
15
|
-
import type { RTCDataChannel } from './rtc-data-channel.js';
|
|
16
|
-
import type { RTCError } from './rtc-error.js';
|
|
17
|
-
|
|
18
|
-
export interface RTCPeerConnectionIceEventInit extends EventInit {
|
|
19
|
-
candidate?: RTCIceCandidate | null;
|
|
20
|
-
url?: string | null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class RTCPeerConnectionIceEvent extends Event {
|
|
24
|
-
readonly candidate: RTCIceCandidate | null;
|
|
25
|
-
readonly url: string | null;
|
|
26
|
-
|
|
27
|
-
constructor(type: string, init: RTCPeerConnectionIceEventInit = {}) {
|
|
28
|
-
super(type, init);
|
|
29
|
-
this.candidate = init.candidate ?? null;
|
|
30
|
-
this.url = init.url ?? null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface RTCDataChannelEventInit extends EventInit {
|
|
35
|
-
channel: RTCDataChannel;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class RTCDataChannelEvent extends Event {
|
|
39
|
-
readonly channel: RTCDataChannel;
|
|
40
|
-
|
|
41
|
-
constructor(type: string, init: RTCDataChannelEventInit) {
|
|
42
|
-
super(type, init);
|
|
43
|
-
if (!init || !init.channel) {
|
|
44
|
-
throw new TypeError('RTCDataChannelEvent requires a `channel` member');
|
|
45
|
-
}
|
|
46
|
-
this.channel = init.channel;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface RTCErrorEventInit extends EventInit {
|
|
51
|
-
error: RTCError;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class RTCErrorEvent extends Event {
|
|
55
|
-
readonly error: RTCError;
|
|
56
|
-
|
|
57
|
-
constructor(type: string, init: RTCErrorEventInit) {
|
|
58
|
-
super(type, init);
|
|
59
|
-
if (!init || !init.error) {
|
|
60
|
-
throw new TypeError('RTCErrorEvent requires an `error` member');
|
|
61
|
-
}
|
|
62
|
-
this.error = init.error;
|
|
63
|
-
}
|
|
64
|
-
}
|
package/src/rtc-ice-candidate.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
// RTCIceCandidate — W3C WebRTC ICE candidate descriptor.
|
|
2
|
-
//
|
|
3
|
-
// Reference: refs/node-gst-webrtc/src/webrtc/RTCIceCandidate.ts (ISC)
|
|
4
|
-
//
|
|
5
|
-
// Field extraction (type/protocol/port/...) uses a minimal SDP candidate-line
|
|
6
|
-
// parser per RFC 5245 §15.1.
|
|
7
|
-
|
|
8
|
-
export interface RTCIceCandidateInit {
|
|
9
|
-
candidate?: string;
|
|
10
|
-
sdpMid?: string | null;
|
|
11
|
-
sdpMLineIndex?: number | null;
|
|
12
|
-
usernameFragment?: string | null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type RTCIceComponent = 'rtp' | 'rtcp';
|
|
16
|
-
export type RTCIceProtocol = 'udp' | 'tcp';
|
|
17
|
-
export type RTCIceCandidateType = 'host' | 'srflx' | 'prflx' | 'relay';
|
|
18
|
-
export type RTCIceTcpCandidateType = 'active' | 'passive' | 'so';
|
|
19
|
-
|
|
20
|
-
interface ParsedCandidate {
|
|
21
|
-
foundation: string;
|
|
22
|
-
component: RTCIceComponent | null;
|
|
23
|
-
priority: number | null;
|
|
24
|
-
protocol: RTCIceProtocol | null;
|
|
25
|
-
address: string | null;
|
|
26
|
-
port: number | null;
|
|
27
|
-
type: RTCIceCandidateType | null;
|
|
28
|
-
relatedAddress: string | null;
|
|
29
|
-
relatedPort: number | null;
|
|
30
|
-
tcpType: RTCIceTcpCandidateType | null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function parseCandidate(line: string): Partial<ParsedCandidate> {
|
|
34
|
-
// Accepts both `candidate:...` and raw `a=candidate:...` forms.
|
|
35
|
-
let s = line.trim();
|
|
36
|
-
if (s.startsWith('a=')) s = s.slice(2);
|
|
37
|
-
if (s.startsWith('candidate:')) s = s.slice('candidate:'.length);
|
|
38
|
-
|
|
39
|
-
const parts = s.split(/\s+/);
|
|
40
|
-
if (parts.length < 8) return {};
|
|
41
|
-
|
|
42
|
-
const protocolRaw = parts[2]?.toLowerCase();
|
|
43
|
-
const typeIdx = parts.indexOf('typ');
|
|
44
|
-
const typeRaw = typeIdx >= 0 ? parts[typeIdx + 1] : undefined;
|
|
45
|
-
|
|
46
|
-
const raddrIdx = parts.indexOf('raddr');
|
|
47
|
-
const rportIdx = parts.indexOf('rport');
|
|
48
|
-
const tcpTypeIdx = parts.indexOf('tcptype');
|
|
49
|
-
|
|
50
|
-
const componentId = Number(parts[1]);
|
|
51
|
-
return {
|
|
52
|
-
foundation: parts[0],
|
|
53
|
-
component: componentId === 1 ? 'rtp' : componentId === 2 ? 'rtcp' : null,
|
|
54
|
-
priority: Number(parts[3]) || null,
|
|
55
|
-
protocol: (protocolRaw === 'udp' || protocolRaw === 'tcp') ? protocolRaw : null,
|
|
56
|
-
address: parts[4] ?? null,
|
|
57
|
-
port: Number(parts[5]) || null,
|
|
58
|
-
type: (typeRaw === 'host' || typeRaw === 'srflx' || typeRaw === 'prflx' || typeRaw === 'relay') ? typeRaw : null,
|
|
59
|
-
relatedAddress: raddrIdx >= 0 ? parts[raddrIdx + 1] ?? null : null,
|
|
60
|
-
relatedPort: rportIdx >= 0 ? Number(parts[rportIdx + 1]) || null : null,
|
|
61
|
-
tcpType: tcpTypeIdx >= 0
|
|
62
|
-
? (parts[tcpTypeIdx + 1] as RTCIceTcpCandidateType)
|
|
63
|
-
: null,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class RTCIceCandidate {
|
|
68
|
-
readonly candidate: string;
|
|
69
|
-
readonly sdpMid: string | null;
|
|
70
|
-
readonly sdpMLineIndex: number | null;
|
|
71
|
-
readonly usernameFragment: string | null;
|
|
72
|
-
readonly foundation: string | null;
|
|
73
|
-
readonly component: RTCIceComponent | null;
|
|
74
|
-
readonly priority: number | null;
|
|
75
|
-
readonly protocol: RTCIceProtocol | null;
|
|
76
|
-
readonly address: string | null;
|
|
77
|
-
readonly port: number | null;
|
|
78
|
-
readonly type: RTCIceCandidateType | null;
|
|
79
|
-
readonly tcpType: RTCIceTcpCandidateType | null;
|
|
80
|
-
readonly relatedAddress: string | null;
|
|
81
|
-
readonly relatedPort: number | null;
|
|
82
|
-
|
|
83
|
-
constructor(init: RTCIceCandidateInit = {}) {
|
|
84
|
-
if (init.sdpMid == null && init.sdpMLineIndex == null) {
|
|
85
|
-
throw new TypeError(
|
|
86
|
-
'RTCIceCandidate requires either sdpMid or sdpMLineIndex',
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
this.candidate = init.candidate ?? '';
|
|
90
|
-
this.sdpMid = init.sdpMid ?? null;
|
|
91
|
-
this.sdpMLineIndex = init.sdpMLineIndex ?? null;
|
|
92
|
-
this.usernameFragment = init.usernameFragment ?? null;
|
|
93
|
-
|
|
94
|
-
const parsed = parseCandidate(this.candidate);
|
|
95
|
-
this.foundation = parsed.foundation ?? null;
|
|
96
|
-
this.component = parsed.component ?? null;
|
|
97
|
-
this.priority = parsed.priority ?? null;
|
|
98
|
-
this.protocol = parsed.protocol ?? null;
|
|
99
|
-
this.address = parsed.address ?? null;
|
|
100
|
-
this.port = parsed.port ?? null;
|
|
101
|
-
this.type = parsed.type ?? null;
|
|
102
|
-
this.tcpType = parsed.tcpType ?? null;
|
|
103
|
-
this.relatedAddress = parsed.relatedAddress ?? null;
|
|
104
|
-
this.relatedPort = parsed.relatedPort ?? null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
toJSON(): RTCIceCandidateInit {
|
|
108
|
-
return {
|
|
109
|
-
candidate: this.candidate,
|
|
110
|
-
sdpMid: this.sdpMid,
|
|
111
|
-
sdpMLineIndex: this.sdpMLineIndex,
|
|
112
|
-
usernameFragment: this.usernameFragment,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
package/src/rtc-ice-transport.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// W3C RTCIceTransport for GJS.
|
|
2
|
-
//
|
|
3
|
-
// Thin wrapper that reflects webrtcbin's ICE state. The RTCPeerConnection
|
|
4
|
-
// updates state/gatheringState via the WebrtcbinBridge signal handlers.
|
|
5
|
-
//
|
|
6
|
-
// Reference: W3C WebRTC spec § 5.6
|
|
7
|
-
// Reference: refs/wpt/webrtc/RTCIceTransport.html
|
|
8
|
-
|
|
9
|
-
import '@gjsify/dom-events/register/event-target';
|
|
10
|
-
|
|
11
|
-
import { RTCIceCandidate, type RTCIceCandidateInit } from './rtc-ice-candidate.js';
|
|
12
|
-
|
|
13
|
-
export type RTCIceRole = 'unknown' | 'controlling' | 'controlled';
|
|
14
|
-
export type RTCIceComponent = 'rtp' | 'rtcp';
|
|
15
|
-
export type RTCIceTransportState =
|
|
16
|
-
| 'new' | 'checking' | 'connected' | 'completed'
|
|
17
|
-
| 'disconnected' | 'failed' | 'closed';
|
|
18
|
-
|
|
19
|
-
export interface RTCIceParameters {
|
|
20
|
-
usernameFragment?: string;
|
|
21
|
-
password?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface RTCIceCandidatePair {
|
|
25
|
-
local: RTCIceCandidate;
|
|
26
|
-
remote: RTCIceCandidate;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
type EventHandler = ((ev: Event) => void) | null;
|
|
30
|
-
|
|
31
|
-
export class RTCIceTransport extends EventTarget {
|
|
32
|
-
private _state: RTCIceTransportState = 'new';
|
|
33
|
-
private _gatheringState: RTCIceGatheringState = 'new';
|
|
34
|
-
private _role: RTCIceRole = 'unknown';
|
|
35
|
-
private _component: RTCIceComponent = 'rtp';
|
|
36
|
-
private _localCandidates: RTCIceCandidate[] = [];
|
|
37
|
-
private _remoteCandidates: RTCIceCandidate[] = [];
|
|
38
|
-
private _localParams: RTCIceParameters | null = null;
|
|
39
|
-
private _remoteParams: RTCIceParameters | null = null;
|
|
40
|
-
|
|
41
|
-
private _onstatechange: EventHandler = null;
|
|
42
|
-
private _ongatheringstatechange: EventHandler = null;
|
|
43
|
-
private _onselectedcandidatepairchange: EventHandler = null;
|
|
44
|
-
|
|
45
|
-
get state(): RTCIceTransportState { return this._state; }
|
|
46
|
-
get gatheringState(): RTCIceGatheringState { return this._gatheringState; }
|
|
47
|
-
get role(): RTCIceRole { return this._role; }
|
|
48
|
-
get component(): RTCIceComponent { return this._component; }
|
|
49
|
-
|
|
50
|
-
get onstatechange(): EventHandler { return this._onstatechange; }
|
|
51
|
-
set onstatechange(v: EventHandler) { this._onstatechange = v; }
|
|
52
|
-
get ongatheringstatechange(): EventHandler { return this._ongatheringstatechange; }
|
|
53
|
-
set ongatheringstatechange(v: EventHandler) { this._ongatheringstatechange = v; }
|
|
54
|
-
get onselectedcandidatepairchange(): EventHandler { return this._onselectedcandidatepairchange; }
|
|
55
|
-
set onselectedcandidatepairchange(v: EventHandler) { this._onselectedcandidatepairchange = v; }
|
|
56
|
-
|
|
57
|
-
getLocalCandidates(): RTCIceCandidate[] { return [...this._localCandidates]; }
|
|
58
|
-
getRemoteCandidates(): RTCIceCandidate[] { return [...this._remoteCandidates]; }
|
|
59
|
-
getSelectedCandidatePair(): RTCIceCandidatePair | null { return null; }
|
|
60
|
-
getLocalParameters(): RTCIceParameters | null { return this._localParams; }
|
|
61
|
-
getRemoteParameters(): RTCIceParameters | null { return this._remoteParams; }
|
|
62
|
-
|
|
63
|
-
// ---- Internal setters (called by RTCPeerConnection) ---------------------
|
|
64
|
-
|
|
65
|
-
/** @internal */
|
|
66
|
-
_setState(state: RTCIceTransportState): void {
|
|
67
|
-
if (this._state === state) return;
|
|
68
|
-
this._state = state;
|
|
69
|
-
const ev = new Event('statechange');
|
|
70
|
-
this._onstatechange?.call(this, ev);
|
|
71
|
-
this.dispatchEvent(ev);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** @internal */
|
|
75
|
-
_setGatheringState(state: RTCIceGatheringState): void {
|
|
76
|
-
if (this._gatheringState === state) return;
|
|
77
|
-
this._gatheringState = state;
|
|
78
|
-
const ev = new Event('gatheringstatechange');
|
|
79
|
-
this._ongatheringstatechange?.call(this, ev);
|
|
80
|
-
this.dispatchEvent(ev);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** @internal */
|
|
84
|
-
_addLocalCandidate(init: RTCIceCandidateInit): void {
|
|
85
|
-
this._localCandidates.push(new RTCIceCandidate(init));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** @internal */
|
|
89
|
-
_addRemoteCandidate(init: RTCIceCandidateInit): void {
|
|
90
|
-
this._remoteCandidates.push(new RTCIceCandidate(init));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** @internal */
|
|
94
|
-
_setLocalParameters(params: RTCIceParameters): void {
|
|
95
|
-
this._localParams = params;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** @internal */
|
|
99
|
-
_setRemoteParameters(params: RTCIceParameters): void {
|
|
100
|
-
this._remoteParams = params;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
type RTCIceGatheringState = 'new' | 'gathering' | 'complete';
|