@gjsify/webrtc 0.3.13 → 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.
- package/lib/esm/get-user-media.js +95 -80
- package/lib/esm/gst-enum-maps.js +55 -59
- package/lib/esm/gst-init.js +19 -22
- package/lib/esm/gst-stats-parser.js +94 -67
- package/lib/esm/gst-utils.js +24 -13
- package/lib/esm/index.js +13 -43
- package/lib/esm/media-device-info.js +23 -22
- package/lib/esm/media-devices.js +150 -139
- package/lib/esm/media-stream-track.js +136 -139
- package/lib/esm/media-stream.js +76 -75
- package/lib/esm/register/data-channel.js +7 -3
- package/lib/esm/register/error.js +6 -2
- package/lib/esm/register/media-devices.js +6 -2
- package/lib/esm/register/media.js +8 -4
- package/lib/esm/register/peer-connection.js +9 -5
- package/lib/esm/rtc-certificate.js +62 -66
- package/lib/esm/rtc-data-channel.js +240 -251
- package/lib/esm/rtc-dtls-transport.js +40 -39
- package/lib/esm/rtc-dtmf-sender.js +92 -100
- package/lib/esm/rtc-error.js +24 -22
- package/lib/esm/rtc-events.js +33 -33
- package/lib/esm/rtc-ice-candidate.js +71 -72
- package/lib/esm/rtc-ice-transport.js +95 -94
- package/lib/esm/rtc-peer-connection.js +796 -845
- package/lib/esm/rtc-rtp-receiver.js +89 -87
- package/lib/esm/rtc-rtp-sender.js +282 -290
- package/lib/esm/rtc-rtp-transceiver.js +92 -93
- package/lib/esm/rtc-sctp-transport.js +38 -38
- package/lib/esm/rtc-session-description.js +47 -51
- package/lib/esm/rtc-stats-report.js +39 -34
- package/lib/esm/rtc-track-event.js +29 -27
- package/lib/esm/rtp-capabilities.js +81 -35
- package/lib/esm/tee-multiplexer.js +58 -60
- package/lib/esm/wpt-helpers.js +128 -112
- package/package.json +13 -13
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,62 +1,60 @@
|
|
|
1
1
|
import { Gst } from "./gst-init.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 };
|
package/lib/esm/wpt-helpers.js
CHANGED
|
@@ -1,122 +1,138 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
130
|
+
for (const pc of pcs) {
|
|
131
|
+
try {
|
|
132
|
+
pc?.close();
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
114
135
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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.
|
|
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.
|
|
59
|
-
"@gjsify/dom-events": "^0.3.
|
|
60
|
-
"@gjsify/dom-exception": "^0.3.
|
|
61
|
-
"@gjsify/webrtc-native": "^0.3.
|
|
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": "
|
|
65
|
-
"@girs/glib-2.0": "
|
|
66
|
-
"@girs/gobject-2.0": "
|
|
67
|
-
"@girs/gst-1.0": "
|
|
68
|
-
"@girs/gstsdp-1.0": "
|
|
69
|
-
"@girs/gstwebrtc-1.0": "
|
|
70
|
-
"@gjsify/cli": "^0.3.
|
|
71
|
-
"@gjsify/unit": "^0.3.
|
|
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
|
}
|