@gjsify/webrtc 0.3.12 → 0.3.14

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.
Files changed (36) hide show
  1. package/lib/esm/get-user-media.js +95 -80
  2. package/lib/esm/gst-enum-maps.js +55 -59
  3. package/lib/esm/gst-init.js +19 -22
  4. package/lib/esm/gst-stats-parser.js +94 -67
  5. package/lib/esm/gst-utils.js +24 -13
  6. package/lib/esm/index.js +13 -43
  7. package/lib/esm/media-device-info.js +23 -22
  8. package/lib/esm/media-devices.js +150 -139
  9. package/lib/esm/media-stream-track.js +136 -139
  10. package/lib/esm/media-stream.js +76 -75
  11. package/lib/esm/register/data-channel.js +7 -3
  12. package/lib/esm/register/error.js +6 -2
  13. package/lib/esm/register/media-devices.js +6 -2
  14. package/lib/esm/register/media.js +8 -4
  15. package/lib/esm/register/peer-connection.js +9 -5
  16. package/lib/esm/rtc-certificate.js +62 -66
  17. package/lib/esm/rtc-data-channel.js +240 -251
  18. package/lib/esm/rtc-dtls-transport.js +40 -39
  19. package/lib/esm/rtc-dtmf-sender.js +92 -100
  20. package/lib/esm/rtc-error.js +24 -22
  21. package/lib/esm/rtc-events.js +33 -33
  22. package/lib/esm/rtc-ice-candidate.js +71 -72
  23. package/lib/esm/rtc-ice-transport.js +95 -94
  24. package/lib/esm/rtc-peer-connection.js +796 -845
  25. package/lib/esm/rtc-rtp-receiver.js +89 -87
  26. package/lib/esm/rtc-rtp-sender.js +282 -290
  27. package/lib/esm/rtc-rtp-transceiver.js +92 -93
  28. package/lib/esm/rtc-sctp-transport.js +38 -38
  29. package/lib/esm/rtc-session-description.js +47 -51
  30. package/lib/esm/rtc-stats-report.js +39 -34
  31. package/lib/esm/rtc-track-event.js +29 -27
  32. package/lib/esm/rtp-capabilities.js +81 -35
  33. package/lib/esm/tee-multiplexer.js +58 -60
  34. package/lib/esm/wpt-helpers.js +128 -112
  35. package/package.json +13 -13
  36. package/tsconfig.tsbuildinfo +1 -1
@@ -1,62 +1,60 @@
1
1
  import { Gst } from "./gst-init.js";
2
- class TeeMultiplexer {
3
- _tee;
4
- // Gst.Element
5
- _pipeline;
6
- // Gst.Pipeline
7
- _branchCount = 0;
8
- /**
9
- * Create a tee in the given pipeline and link it to the source's output.
10
- * The source must already be in the pipeline.
11
- */
12
- constructor(pipeline, source) {
13
- this._pipeline = pipeline;
14
- this._tee = Gst.ElementFactory.make("tee", null);
15
- this._tee.allow_not_linked = true;
16
- pipeline.add(this._tee);
17
- this._tee.sync_state_with_parent();
18
- source.link(this._tee);
19
- }
20
- /** Request a new src pad from the tee for a consumer branch. */
21
- requestSrcPad() {
22
- const padName = "src_%u";
23
- const srcPad = this._tee.request_pad_simple ? this._tee.request_pad_simple(padName) : this._tee.get_request_pad(padName);
24
- if (srcPad) this._branchCount++;
25
- return srcPad;
26
- }
27
- /**
28
- * Release a branch's src pad from the tee.
29
- * Adds a DROP probe before unlinking to prevent errors.
30
- */
31
- releaseSrcPad(srcPad) {
32
- if (!srcPad) return;
33
- try {
34
- srcPad.add_probe(
35
- Gst.PadProbeType.BLOCK_DOWNSTREAM,
36
- () => Gst.PadProbeReturn.DROP
37
- );
38
- } catch {
39
- }
40
- try {
41
- const peer = srcPad.get_peer();
42
- if (peer) srcPad.unlink(peer);
43
- } catch {
44
- }
45
- try {
46
- this._tee.release_request_pad(srcPad);
47
- } catch {
48
- }
49
- this._branchCount--;
50
- }
51
- /** Number of active branches. */
52
- get branchCount() {
53
- return this._branchCount;
54
- }
55
- /** The tee element (for pipeline queries). */
56
- get element() {
57
- return this._tee;
58
- }
59
- }
60
- export {
61
- TeeMultiplexer
2
+
3
+ //#region src/tee-multiplexer.ts
4
+ /**
5
+ * Manages a GStreamer `tee` element that fans out one source to multiple
6
+ * consumer branches. Each branch gets its own src pad from the tee.
7
+ */
8
+ var TeeMultiplexer = class {
9
+ _tee;
10
+ _pipeline;
11
+ _branchCount = 0;
12
+ /**
13
+ * Create a tee in the given pipeline and link it to the source's output.
14
+ * The source must already be in the pipeline.
15
+ */
16
+ constructor(pipeline, source) {
17
+ this._pipeline = pipeline;
18
+ this._tee = Gst.ElementFactory.make("tee", null);
19
+ this._tee.allow_not_linked = true;
20
+ pipeline.add(this._tee);
21
+ this._tee.sync_state_with_parent();
22
+ source.link(this._tee);
23
+ }
24
+ /** Request a new src pad from the tee for a consumer branch. */
25
+ requestSrcPad() {
26
+ const padName = "src_%u";
27
+ const srcPad = this._tee.request_pad_simple ? this._tee.request_pad_simple(padName) : this._tee.get_request_pad(padName);
28
+ if (srcPad) this._branchCount++;
29
+ return srcPad;
30
+ }
31
+ /**
32
+ * Release a branch's src pad from the tee.
33
+ * Adds a DROP probe before unlinking to prevent errors.
34
+ */
35
+ releaseSrcPad(srcPad) {
36
+ if (!srcPad) return;
37
+ try {
38
+ srcPad.add_probe(Gst.PadProbeType.BLOCK_DOWNSTREAM, () => Gst.PadProbeReturn.DROP);
39
+ } catch {}
40
+ try {
41
+ const peer = srcPad.get_peer();
42
+ if (peer) srcPad.unlink(peer);
43
+ } catch {}
44
+ try {
45
+ this._tee.release_request_pad(srcPad);
46
+ } catch {}
47
+ this._branchCount--;
48
+ }
49
+ /** Number of active branches. */
50
+ get branchCount() {
51
+ return this._branchCount;
52
+ }
53
+ /** The tee element (for pipeline queries). */
54
+ get element() {
55
+ return this._tee;
56
+ }
62
57
  };
58
+
59
+ //#endregion
60
+ export { TeeMultiplexer };
@@ -1,122 +1,138 @@
1
- import {
2
- RTCPeerConnection
3
- } from "./index.js";
1
+ import { RTCPeerConnection } from "./rtc-peer-connection.js";
2
+ import "./index.js";
3
+
4
+ //#region src/wpt-helpers.ts
5
+ /** Mirror WPT's `createPeerConnectionWithCleanup` — returns a fresh pc. */
4
6
  function createPeerConnection() {
5
- return new RTCPeerConnection();
7
+ return new RTCPeerConnection();
6
8
  }
9
+ /**
10
+ * Mirror WPT's `exchangeOfferAnswer(pc1, pc2)` + `exchangeIceCandidates`.
11
+ * Runs the full handshake to completion.
12
+ */
7
13
  async function exchangeOfferAnswer(pc1, pc2) {
8
- pc1.onicecandidate = (ev) => {
9
- if (ev.candidate) pc2.addIceCandidate(ev.candidate.toJSON()).catch(() => {
10
- });
11
- };
12
- pc2.onicecandidate = (ev) => {
13
- if (ev.candidate) pc1.addIceCandidate(ev.candidate.toJSON()).catch(() => {
14
- });
15
- };
16
- const offer = await pc1.createOffer();
17
- await pc1.setLocalDescription(offer);
18
- await pc2.setRemoteDescription(offer);
19
- const answer = await pc2.createAnswer();
20
- await pc2.setLocalDescription(answer);
21
- await pc1.setRemoteDescription(answer);
14
+ pc1.onicecandidate = (ev) => {
15
+ if (ev.candidate) pc2.addIceCandidate(ev.candidate.toJSON()).catch(() => {});
16
+ };
17
+ pc2.onicecandidate = (ev) => {
18
+ if (ev.candidate) pc1.addIceCandidate(ev.candidate.toJSON()).catch(() => {});
19
+ };
20
+ const offer = await pc1.createOffer();
21
+ await pc1.setLocalDescription(offer);
22
+ await pc2.setRemoteDescription(offer);
23
+ const answer = await pc2.createAnswer();
24
+ await pc2.setLocalDescription(answer);
25
+ await pc1.setRemoteDescription(answer);
22
26
  }
27
+ /**
28
+ * Port of WPT `createDataChannelPair(t, options, pc1, pc2)` — returns
29
+ * `[dc1, dc2]` both in `'open'` state, handshake complete. If `options.negotiated`
30
+ * both sides pre-create the channel with matching id; otherwise pc1 creates
31
+ * and pc2 receives via `ondatachannel`.
32
+ */
23
33
  async function createDataChannelPair(options = {}, pc1 = createPeerConnection(), pc2 = createPeerConnection(), label = "wpt") {
24
- let pair;
25
- let bothOpen;
26
- if (options.negotiated) {
27
- const dc1 = pc1.createDataChannel(label, options);
28
- const dc2 = pc2.createDataChannel(label, options);
29
- pair = [dc1, dc2];
30
- bothOpen = Promise.all(pair.map((dc) => new Promise((res, rej) => {
31
- if (dc.readyState === "open") return res();
32
- dc.onopen = () => res();
33
- dc.onerror = (ev) => rej(ev?.error ?? new Error("onerror"));
34
- })));
35
- } else {
36
- const dc1 = pc1.createDataChannel(label, options);
37
- bothOpen = Promise.all([
38
- new Promise((res, rej) => {
39
- if (dc1.readyState === "open") return res();
40
- dc1.onopen = () => res();
41
- dc1.onerror = (ev) => rej(ev?.error ?? new Error("onerror"));
42
- }),
43
- new Promise((res, rej) => {
44
- pc2.ondatachannel = (ev) => {
45
- const dc2 = ev.channel;
46
- if (dc2.readyState === "open") return res(dc2);
47
- dc2.onopen = () => res(dc2);
48
- dc2.onerror = (e) => rej(e?.error ?? new Error("onerror"));
49
- };
50
- }).then((dc2) => {
51
- pair[1] = dc2;
52
- })
53
- ]);
54
- pair = [dc1, void 0];
55
- }
56
- await exchangeOfferAnswer(pc1, pc2);
57
- await bothOpen;
58
- return [pair[0], pair[1], pc1, pc2];
34
+ let pair;
35
+ let bothOpen;
36
+ if (options.negotiated) {
37
+ const dc1 = pc1.createDataChannel(label, options);
38
+ const dc2 = pc2.createDataChannel(label, options);
39
+ pair = [dc1, dc2];
40
+ bothOpen = Promise.all(pair.map((dc) => new Promise((res, rej) => {
41
+ if (dc.readyState === "open") return res();
42
+ dc.onopen = () => res();
43
+ dc.onerror = (ev) => rej(ev?.error ?? new Error("onerror"));
44
+ })));
45
+ } else {
46
+ const dc1 = pc1.createDataChannel(label, options);
47
+ bothOpen = Promise.all([new Promise((res, rej) => {
48
+ if (dc1.readyState === "open") return res();
49
+ dc1.onopen = () => res();
50
+ dc1.onerror = (ev) => rej(ev?.error ?? new Error("onerror"));
51
+ }), new Promise((res, rej) => {
52
+ pc2.ondatachannel = (ev) => {
53
+ const dc2 = ev.channel;
54
+ if (dc2.readyState === "open") return res(dc2);
55
+ dc2.onopen = () => res(dc2);
56
+ dc2.onerror = (e) => rej(e?.error ?? new Error("onerror"));
57
+ };
58
+ }).then((dc2) => {
59
+ pair[1] = dc2;
60
+ })]);
61
+ pair = [dc1, undefined];
62
+ }
63
+ await exchangeOfferAnswer(pc1, pc2);
64
+ await bothOpen;
65
+ return [
66
+ pair[0],
67
+ pair[1],
68
+ pc1,
69
+ pc2
70
+ ];
59
71
  }
72
+ /** Port of WPT `awaitMessage(channel)` — resolves with the next incoming data. */
60
73
  function awaitMessage(channel) {
61
- return new Promise((resolve, reject) => {
62
- const messageHandler = (ev) => {
63
- channel.removeEventListener("message", messageHandler);
64
- channel.removeEventListener("error", errorHandler);
65
- resolve(ev.data);
66
- };
67
- const errorHandler = (ev) => {
68
- channel.removeEventListener("message", messageHandler);
69
- channel.removeEventListener("error", errorHandler);
70
- reject(ev?.error ?? new Error("channel error"));
71
- };
72
- channel.addEventListener("message", messageHandler);
73
- channel.addEventListener("error", errorHandler);
74
- });
75
- }
76
- class EventWatcher {
77
- _events = [];
78
- _waiters = [];
79
- constructor(target, eventTypes) {
80
- for (const type of eventTypes) {
81
- target.addEventListener(type, () => {
82
- this._events.push(type);
83
- this._tryResolve();
84
- });
85
- }
86
- }
87
- wait_for(expected) {
88
- const types = Array.isArray(expected) ? expected : [expected];
89
- return new Promise((resolve, reject) => {
90
- this._waiters.push({ types, resolve, reject });
91
- this._tryResolve();
92
- });
93
- }
94
- _tryResolve() {
95
- while (this._waiters.length > 0 && this._events.length >= this._waiters[0].types.length) {
96
- const waiter = this._waiters.shift();
97
- const got = this._events.splice(0, waiter.types.length);
98
- const matches = waiter.types.every((t, i) => got[i] === t);
99
- if (matches) {
100
- waiter.resolve();
101
- } else {
102
- waiter.reject(new Error(`EventWatcher: expected ${JSON.stringify(waiter.types)}, got ${JSON.stringify(got)}`));
103
- }
104
- }
105
- }
74
+ return new Promise((resolve, reject) => {
75
+ const messageHandler = (ev) => {
76
+ channel.removeEventListener("message", messageHandler);
77
+ channel.removeEventListener("error", errorHandler);
78
+ resolve(ev.data);
79
+ };
80
+ const errorHandler = (ev) => {
81
+ channel.removeEventListener("message", messageHandler);
82
+ channel.removeEventListener("error", errorHandler);
83
+ reject(ev?.error ?? new Error("channel error"));
84
+ };
85
+ channel.addEventListener("message", messageHandler);
86
+ channel.addEventListener("error", errorHandler);
87
+ });
106
88
  }
89
+ /**
90
+ * Mirror WPT's `EventWatcher(t, target, events)` — accumulates events in
91
+ * order and returns `wait_for(types)` that matches the expected sequence.
92
+ */
93
+ var EventWatcher = class {
94
+ _events = [];
95
+ _waiters = [];
96
+ constructor(target, eventTypes) {
97
+ for (const type of eventTypes) {
98
+ target.addEventListener(type, () => {
99
+ this._events.push(type);
100
+ this._tryResolve();
101
+ });
102
+ }
103
+ }
104
+ wait_for(expected) {
105
+ const types = Array.isArray(expected) ? expected : [expected];
106
+ return new Promise((resolve, reject) => {
107
+ this._waiters.push({
108
+ types,
109
+ resolve,
110
+ reject
111
+ });
112
+ this._tryResolve();
113
+ });
114
+ }
115
+ _tryResolve() {
116
+ while (this._waiters.length > 0 && this._events.length >= this._waiters[0].types.length) {
117
+ const waiter = this._waiters.shift();
118
+ const got = this._events.splice(0, waiter.types.length);
119
+ const matches = waiter.types.every((t, i) => got[i] === t);
120
+ if (matches) {
121
+ waiter.resolve();
122
+ } else {
123
+ waiter.reject(new Error(`EventWatcher: expected ${JSON.stringify(waiter.types)}, got ${JSON.stringify(got)}`));
124
+ }
125
+ }
126
+ }
127
+ };
128
+ /** Close an array of RTCPeerConnection instances, ignoring errors. */
107
129
  function closePeerConnections(...pcs) {
108
- for (const pc of pcs) {
109
- try {
110
- pc?.close();
111
- } catch {
112
- }
113
- }
130
+ for (const pc of pcs) {
131
+ try {
132
+ pc?.close();
133
+ } catch {}
134
+ }
114
135
  }
115
- export {
116
- EventWatcher,
117
- awaitMessage,
118
- closePeerConnections,
119
- createDataChannelPair,
120
- createPeerConnection,
121
- exchangeOfferAnswer
122
- };
136
+
137
+ //#endregion
138
+ export { EventWatcher, awaitMessage, closePeerConnections, createDataChannelPair, createPeerConnection, exchangeOfferAnswer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/webrtc",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "W3C WebRTC API for GJS using GStreamer webrtcbin as the peer-connection backend",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -55,20 +55,20 @@
55
55
  "peer-connection"
56
56
  ],
57
57
  "dependencies": {
58
- "@gjsify/buffer": "^0.3.12",
59
- "@gjsify/dom-events": "^0.3.12",
60
- "@gjsify/dom-exception": "^0.3.12",
61
- "@gjsify/webrtc-native": "^0.3.12"
58
+ "@gjsify/buffer": "^0.3.14",
59
+ "@gjsify/dom-events": "^0.3.14",
60
+ "@gjsify/dom-exception": "^0.3.14",
61
+ "@gjsify/webrtc-native": "^0.3.14"
62
62
  },
63
63
  "devDependencies": {
64
- "@girs/gjs": "^4.0.0-rc.9",
65
- "@girs/glib-2.0": "^2.88.0-4.0.0-rc.9",
66
- "@girs/gobject-2.0": "^2.88.0-4.0.0-rc.9",
67
- "@girs/gst-1.0": "^1.28.1-4.0.0-rc.9",
68
- "@girs/gstsdp-1.0": "^1.0.0-4.0.0-rc.9",
69
- "@girs/gstwebrtc-1.0": "^1.0.0-4.0.0-rc.9",
70
- "@gjsify/cli": "^0.3.12",
71
- "@gjsify/unit": "^0.3.12",
64
+ "@girs/gjs": "4.0.0-rc.9",
65
+ "@girs/glib-2.0": "2.88.0-4.0.0-rc.9",
66
+ "@girs/gobject-2.0": "2.88.0-4.0.0-rc.9",
67
+ "@girs/gst-1.0": "1.28.1-4.0.0-rc.9",
68
+ "@girs/gstsdp-1.0": "1.0.0-4.0.0-rc.9",
69
+ "@girs/gstwebrtc-1.0": "1.0.0-4.0.0-rc.9",
70
+ "@gjsify/cli": "^0.3.14",
71
+ "@gjsify/unit": "^0.3.14",
72
72
  "@types/node": "^25.6.0",
73
73
  "typescript": "^6.0.3"
74
74
  }