werift 0.23.0 → 0.24.0
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/README.md +46 -0
- package/lib/common/src/transport.d.ts +22 -9
- package/lib/common/src/transport.js +152 -25
- package/lib/common/src/transport.js.map +1 -1
- package/lib/ice/src/candidate.d.ts +16 -1
- package/lib/ice/src/candidate.js +99 -15
- package/lib/ice/src/candidate.js.map +1 -1
- package/lib/ice/src/ice.d.ts +2 -0
- package/lib/ice/src/ice.js +171 -41
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +23 -2
- package/lib/ice/src/iceBase.js +63 -1
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/index.d.ts +2 -0
- package/lib/ice/src/index.js +2 -0
- package/lib/ice/src/index.js.map +1 -1
- package/lib/ice/src/stun/const.d.ts +1 -26
- package/lib/ice/src/stun/const.js +15 -54
- package/lib/ice/src/stun/const.js.map +1 -1
- package/lib/ice/src/stun/message.d.ts +1 -25
- package/lib/ice/src/stun/message.js +15 -149
- package/lib/ice/src/stun/message.js.map +1 -1
- package/lib/ice/src/stun/protocol.d.ts +1 -1
- package/lib/ice/src/stun/protocol.js +2 -2
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpFrame.d.ts +5 -0
- package/lib/ice/src/stun/tcpFrame.js +26 -0
- package/lib/ice/src/stun/tcpFrame.js.map +1 -0
- package/lib/ice/src/stun/tcpProtocol.d.ts +51 -0
- package/lib/ice/src/stun/tcpProtocol.js +326 -0
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -0
- package/lib/ice/src/stun/transaction.d.ts +2 -1
- package/lib/ice/src/stun/transaction.js +9 -3
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/frame.d.ts +1 -0
- package/lib/ice/src/turn/frame.js +18 -0
- package/lib/ice/src/turn/frame.js.map +1 -0
- package/lib/ice/src/turn/protocol.d.ts +9 -7
- package/lib/ice/src/turn/protocol.js +32 -44
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +1 -1
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/{ice → ice-server}/src/stun/attributes.d.ts +12 -5
- package/lib/{ice → ice-server}/src/stun/attributes.js +60 -19
- package/lib/ice-server/src/stun/attributes.js.map +1 -0
- package/lib/ice-server/src/stun/const.d.ts +28 -0
- package/lib/ice-server/src/stun/const.js +88 -0
- package/lib/ice-server/src/stun/const.js.map +1 -0
- package/lib/ice-server/src/stun/message.d.ts +37 -0
- package/lib/ice-server/src/stun/message.js +190 -0
- package/lib/ice-server/src/stun/message.js.map +1 -0
- package/lib/ice-server/src/turn/auth.d.ts +1 -0
- package/lib/ice-server/src/turn/auth.js +10 -0
- package/lib/ice-server/src/turn/auth.js.map +1 -0
- package/lib/ice-server/src/turn/frame.d.ts +13 -0
- package/lib/ice-server/src/turn/frame.js +69 -0
- package/lib/ice-server/src/turn/frame.js.map +1 -0
- package/lib/index.mjs +2688 -855
- package/lib/nonstandard/index.mjs +1753 -403
- package/lib/rtp/src/extra/container/mp4/container.d.ts +9 -6
- package/lib/rtp/src/extra/container/mp4/container.js +258 -173
- package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
- package/lib/rtp/src/extra/container/mp4/h264.d.ts +1 -0
- package/lib/rtp/src/extra/container/mp4/h264.js +13 -0
- package/lib/rtp/src/extra/container/mp4/h264.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4.d.ts +11 -7
- package/lib/rtp/src/extra/processor/mp4.js +115 -60
- package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4Callback.d.ts +2 -3
- package/lib/rtp/src/extra/processor/mp4Callback.js +5 -3
- package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
- package/lib/webrtc/src/dataChannel.d.ts +3 -2
- package/lib/webrtc/src/dataChannel.js +9 -2
- package/lib/webrtc/src/dataChannel.js.map +1 -1
- package/lib/webrtc/src/errors.d.ts +3 -0
- package/lib/webrtc/src/errors.js +11 -0
- package/lib/webrtc/src/errors.js.map +1 -0
- package/lib/webrtc/src/helper.d.ts +5 -1
- package/lib/webrtc/src/helper.js +33 -1
- package/lib/webrtc/src/helper.js.map +1 -1
- package/lib/webrtc/src/media/receiver/statistics.d.ts +4 -1
- package/lib/webrtc/src/media/receiver/statistics.js +22 -0
- package/lib/webrtc/src/media/receiver/statistics.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +15 -2
- package/lib/webrtc/src/media/rtpReceiver.js +135 -20
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +32 -7
- package/lib/webrtc/src/media/rtpSender.js +177 -28
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +13 -4
- package/lib/webrtc/src/media/rtpTransceiver.js +26 -5
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/stats.d.ts +15 -1
- package/lib/webrtc/src/media/stats.js +55 -1
- package/lib/webrtc/src/media/stats.js.map +1 -1
- package/lib/webrtc/src/media/track.d.ts +2 -1
- package/lib/webrtc/src/media/track.js +7 -1
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/dummyMedia.d.ts +19 -0
- package/lib/webrtc/src/nonstandard/dummyMedia.js +157 -0
- package/lib/webrtc/src/nonstandard/dummyMedia.js.map +1 -0
- package/lib/webrtc/src/nonstandard/navigator.d.ts +18 -8
- package/lib/webrtc/src/nonstandard/navigator.js +96 -32
- package/lib/webrtc/src/nonstandard/navigator.js.map +1 -1
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.d.ts +16 -0
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.js +360 -0
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.js.map +1 -0
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +71 -31
- package/lib/webrtc/src/nonstandard/userMedia.js +454 -108
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +183 -50
- package/lib/webrtc/src/peerConnection.js +458 -109
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sctpManager.d.ts +1 -1
- package/lib/webrtc/src/sctpManager.js +27 -7
- package/lib/webrtc/src/sctpManager.js.map +1 -1
- package/lib/webrtc/src/sdp.d.ts +10 -7
- package/lib/webrtc/src/sdp.js +39 -9
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/sdpManager.d.ts +4 -3
- package/lib/webrtc/src/sdpManager.js +50 -27
- package/lib/webrtc/src/sdpManager.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +13 -4
- package/lib/webrtc/src/secureTransportManager.js +100 -22
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transceiverManager.d.ts +5 -4
- package/lib/webrtc/src/transceiverManager.js +61 -62
- package/lib/webrtc/src/transceiverManager.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +15 -4
- package/lib/webrtc/src/transport/dtls.js +91 -23
- package/lib/webrtc/src/transport/dtls.js.map +1 -1
- package/lib/webrtc/src/transport/ice.d.ts +21 -3
- package/lib/webrtc/src/transport/ice.js +159 -30
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +1 -0
- package/lib/webrtc/src/transport/sctp.js +9 -6
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +12 -4
- package/lib/webrtc/src/utils.js +118 -16
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +7 -4
- package/lib/ice/src/stun/attributes.js.map +0 -1
- package/lib/rtp/src/extra/container/mp4/mp4box.d.ts +0 -5
- package/lib/rtp/src/extra/container/mp4/mp4box.js +0 -32
- package/lib/rtp/src/extra/container/mp4/mp4box.js.map +0 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDummyAudioTrack = createDummyAudioTrack;
|
|
4
|
+
exports.createDummyVideoTrack = createDummyVideoTrack;
|
|
5
|
+
const rtp_1 = require("../imports/rtp");
|
|
6
|
+
const track_1 = require("../media/track");
|
|
7
|
+
const AUDIO_PACKET_MS = 20;
|
|
8
|
+
const AUDIO_TIMESTAMP_STEP = 960;
|
|
9
|
+
const AUDIO_PAYLOAD = Buffer.from([0xf8, 0xff, 0xfe]);
|
|
10
|
+
const DEFAULT_VIDEO_FPS = 30;
|
|
11
|
+
const VIDEO_TIMESTAMP_STEP = 3000;
|
|
12
|
+
const DUMMY_SSRC_BASE = 0x1a2b3c00;
|
|
13
|
+
const DUMMY_SEQUENCE_BASE = 0x4000;
|
|
14
|
+
const DUMMY_SEQUENCE_STEP = 0x0100;
|
|
15
|
+
let nextDummySourceId = 0;
|
|
16
|
+
const VP8_KEYFRAME = Buffer.from([
|
|
17
|
+
0x10, 0x10, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x02, 0x00, 0x02, 0x00,
|
|
18
|
+
]);
|
|
19
|
+
const VP8_DELTA_FRAME = Buffer.from([0x10, 0x11, 0x00, 0x00]);
|
|
20
|
+
function createDummyAudioTrack(options = {}) {
|
|
21
|
+
const track = new track_1.MediaStreamTrack({ kind: "audio" });
|
|
22
|
+
const source = new ScheduledRtpSource({
|
|
23
|
+
track,
|
|
24
|
+
intervalMs: options.packetIntervalMs ?? AUDIO_PACKET_MS,
|
|
25
|
+
buildPacket: ({ sequenceNumber, timestamp, ssrc }) => new rtp_1.RtpPacket(new rtp_1.RtpHeader({
|
|
26
|
+
version: 2,
|
|
27
|
+
payloadType: 111,
|
|
28
|
+
sequenceNumber,
|
|
29
|
+
timestamp,
|
|
30
|
+
ssrc,
|
|
31
|
+
marker: true,
|
|
32
|
+
}), Buffer.from(AUDIO_PAYLOAD)),
|
|
33
|
+
nextTimestamp: (timestamp) => timestamp + AUDIO_TIMESTAMP_STEP,
|
|
34
|
+
});
|
|
35
|
+
source.start();
|
|
36
|
+
return { track, source };
|
|
37
|
+
}
|
|
38
|
+
function createDummyVideoTrack(options = {}) {
|
|
39
|
+
const fps = options.fps ?? DEFAULT_VIDEO_FPS;
|
|
40
|
+
const keyframeIntervalFrames = options.keyframeIntervalFrames ?? fps;
|
|
41
|
+
const intervalMs = 1000 / fps;
|
|
42
|
+
const track = new track_1.MediaStreamTrack({ kind: "video" });
|
|
43
|
+
const source = new ScheduledRtpSource({
|
|
44
|
+
track,
|
|
45
|
+
intervalMs,
|
|
46
|
+
buildPacket: ({ sequenceNumber, timestamp, ssrc, iteration }) => {
|
|
47
|
+
const isKeyframe = iteration % keyframeIntervalFrames === 0;
|
|
48
|
+
return new rtp_1.RtpPacket(new rtp_1.RtpHeader({
|
|
49
|
+
version: 2,
|
|
50
|
+
payloadType: 96,
|
|
51
|
+
sequenceNumber,
|
|
52
|
+
timestamp,
|
|
53
|
+
ssrc,
|
|
54
|
+
marker: true,
|
|
55
|
+
}), Buffer.from(isKeyframe ? VP8_KEYFRAME : VP8_DELTA_FRAME));
|
|
56
|
+
},
|
|
57
|
+
nextTimestamp: (timestamp) => timestamp + VIDEO_TIMESTAMP_STEP,
|
|
58
|
+
});
|
|
59
|
+
source.start();
|
|
60
|
+
return { track, source };
|
|
61
|
+
}
|
|
62
|
+
class ScheduledRtpSource {
|
|
63
|
+
constructor(props) {
|
|
64
|
+
Object.defineProperty(this, "props", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true,
|
|
68
|
+
value: props
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(this, "sourceId", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: nextDummySourceId++
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "ssrc", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
value: (DUMMY_SSRC_BASE + this.sourceId) >>> 0
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(this, "sequenceNumber", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
configurable: true,
|
|
85
|
+
writable: true,
|
|
86
|
+
value: (DUMMY_SEQUENCE_BASE + this.sourceId * DUMMY_SEQUENCE_STEP) & 0xffff
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(this, "timestamp", {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
configurable: true,
|
|
91
|
+
writable: true,
|
|
92
|
+
value: 0
|
|
93
|
+
});
|
|
94
|
+
Object.defineProperty(this, "iteration", {
|
|
95
|
+
enumerable: true,
|
|
96
|
+
configurable: true,
|
|
97
|
+
writable: true,
|
|
98
|
+
value: 0
|
|
99
|
+
});
|
|
100
|
+
Object.defineProperty(this, "nextTickAt", {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
configurable: true,
|
|
103
|
+
writable: true,
|
|
104
|
+
value: void 0
|
|
105
|
+
});
|
|
106
|
+
Object.defineProperty(this, "timer", {
|
|
107
|
+
enumerable: true,
|
|
108
|
+
configurable: true,
|
|
109
|
+
writable: true,
|
|
110
|
+
value: void 0
|
|
111
|
+
});
|
|
112
|
+
Object.defineProperty(this, "stopped", {
|
|
113
|
+
enumerable: true,
|
|
114
|
+
configurable: true,
|
|
115
|
+
writable: true,
|
|
116
|
+
value: false
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
start() {
|
|
120
|
+
this.nextTickAt = performance.now();
|
|
121
|
+
this.schedule();
|
|
122
|
+
}
|
|
123
|
+
stop() {
|
|
124
|
+
this.stopped = true;
|
|
125
|
+
if (this.timer) {
|
|
126
|
+
clearTimeout(this.timer);
|
|
127
|
+
this.timer = undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
schedule() {
|
|
131
|
+
if (this.stopped || this.props.track.stopped) {
|
|
132
|
+
this.stop();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.nextTickAt =
|
|
136
|
+
(this.nextTickAt ?? performance.now()) + this.props.intervalMs;
|
|
137
|
+
const delay = Math.max(0, this.nextTickAt - performance.now());
|
|
138
|
+
this.timer = setTimeout(() => {
|
|
139
|
+
if (this.stopped || this.props.track.stopped) {
|
|
140
|
+
this.stop();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const packet = this.props.buildPacket({
|
|
144
|
+
sequenceNumber: this.sequenceNumber,
|
|
145
|
+
timestamp: this.timestamp,
|
|
146
|
+
ssrc: this.ssrc,
|
|
147
|
+
iteration: this.iteration,
|
|
148
|
+
});
|
|
149
|
+
this.props.track.writeRtp(packet);
|
|
150
|
+
this.sequenceNumber = (this.sequenceNumber + 1) & 0xffff;
|
|
151
|
+
this.timestamp = this.props.nextTimestamp(this.timestamp) >>> 0;
|
|
152
|
+
this.iteration += 1;
|
|
153
|
+
this.schedule();
|
|
154
|
+
}, delay);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=dummyMedia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dummyMedia.js","sourceRoot":"","sources":["../../../../src/nonstandard/dummyMedia.ts"],"names":[],"mappings":"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","sourcesContent":["import { RtpHeader, RtpPacket } from \"../imports/rtp\";\nimport { MediaStreamTrack } from \"../media/track\";\n\nconst AUDIO_PACKET_MS = 20;\nconst AUDIO_TIMESTAMP_STEP = 960;\nconst AUDIO_PAYLOAD = Buffer.from([0xf8, 0xff, 0xfe]);\n\nconst DEFAULT_VIDEO_FPS = 30;\nconst VIDEO_TIMESTAMP_STEP = 3_000;\nconst DUMMY_SSRC_BASE = 0x1a2b3c00;\nconst DUMMY_SEQUENCE_BASE = 0x4000;\nconst DUMMY_SEQUENCE_STEP = 0x0100;\nlet nextDummySourceId = 0;\n\nconst VP8_KEYFRAME = Buffer.from([\n 0x10, 0x10, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x02, 0x00, 0x02, 0x00,\n]);\nconst VP8_DELTA_FRAME = Buffer.from([0x10, 0x11, 0x00, 0x00]);\n\nexport interface DummyAudioOptions {\n packetIntervalMs: number;\n}\n\nexport interface DummyVideoOptions {\n fps: number;\n keyframeIntervalFrames: number;\n}\n\nexport interface DummySource {\n stop(): void;\n}\n\nexport function createDummyAudioTrack(\n options: Partial<DummyAudioOptions> = {},\n): { track: MediaStreamTrack; source: DummySource } {\n const track = new MediaStreamTrack({ kind: \"audio\" });\n const source = new ScheduledRtpSource({\n track,\n intervalMs: options.packetIntervalMs ?? AUDIO_PACKET_MS,\n buildPacket: ({ sequenceNumber, timestamp, ssrc }) =>\n new RtpPacket(\n new RtpHeader({\n version: 2,\n payloadType: 111,\n sequenceNumber,\n timestamp,\n ssrc,\n marker: true,\n }),\n Buffer.from(AUDIO_PAYLOAD),\n ),\n nextTimestamp: (timestamp) => timestamp + AUDIO_TIMESTAMP_STEP,\n });\n source.start();\n return { track, source };\n}\n\nexport function createDummyVideoTrack(\n options: Partial<DummyVideoOptions> = {},\n): { track: MediaStreamTrack; source: DummySource } {\n const fps = options.fps ?? DEFAULT_VIDEO_FPS;\n const keyframeIntervalFrames = options.keyframeIntervalFrames ?? fps;\n const intervalMs = 1_000 / fps;\n const track = new MediaStreamTrack({ kind: \"video\" });\n\n const source = new ScheduledRtpSource({\n track,\n intervalMs,\n buildPacket: ({ sequenceNumber, timestamp, ssrc, iteration }) => {\n const isKeyframe = iteration % keyframeIntervalFrames === 0;\n return new RtpPacket(\n new RtpHeader({\n version: 2,\n payloadType: 96,\n sequenceNumber,\n timestamp,\n ssrc,\n marker: true,\n }),\n Buffer.from(isKeyframe ? VP8_KEYFRAME : VP8_DELTA_FRAME),\n );\n },\n nextTimestamp: (timestamp) => timestamp + VIDEO_TIMESTAMP_STEP,\n });\n source.start();\n return { track, source };\n}\n\nclass ScheduledRtpSource implements DummySource {\n private readonly sourceId = nextDummySourceId++;\n private readonly ssrc = (DUMMY_SSRC_BASE + this.sourceId) >>> 0;\n private sequenceNumber =\n (DUMMY_SEQUENCE_BASE + this.sourceId * DUMMY_SEQUENCE_STEP) & 0xffff;\n private timestamp = 0;\n private iteration = 0;\n private nextTickAt?: number;\n private timer?: NodeJS.Timeout;\n private stopped = false;\n\n constructor(\n private readonly props: {\n track: MediaStreamTrack;\n intervalMs: number;\n buildPacket(args: {\n sequenceNumber: number;\n timestamp: number;\n ssrc: number;\n iteration: number;\n }): RtpPacket;\n nextTimestamp(timestamp: number): number;\n },\n ) {}\n\n start() {\n this.nextTickAt = performance.now();\n this.schedule();\n }\n\n stop() {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private schedule() {\n if (this.stopped || this.props.track.stopped) {\n this.stop();\n return;\n }\n\n this.nextTickAt =\n (this.nextTickAt ?? performance.now()) + this.props.intervalMs;\n const delay = Math.max(0, this.nextTickAt - performance.now());\n this.timer = setTimeout(() => {\n if (this.stopped || this.props.track.stopped) {\n this.stop();\n return;\n }\n\n const packet = this.props.buildPacket({\n sequenceNumber: this.sequenceNumber,\n timestamp: this.timestamp,\n ssrc: this.ssrc,\n iteration: this.iteration,\n });\n this.props.track.writeRtp(packet);\n\n this.sequenceNumber = (this.sequenceNumber + 1) & 0xffff;\n this.timestamp = this.props.nextTimestamp(this.timestamp) >>> 0;\n this.iteration += 1;\n this.schedule();\n }, delay);\n }\n}\n"]}
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import { RTCRtpCodecParameters } from "..";
|
|
2
2
|
import { MediaStream, MediaStreamTrack } from "../media/track";
|
|
3
|
+
import { type DummyAudioOptions, type DummyVideoOptions } from "./dummyMedia";
|
|
3
4
|
export declare class Navigator {
|
|
4
5
|
mediaDevices: MediaDevices;
|
|
5
6
|
constructor(props?: ConstructorParameters<typeof MediaDevices>[0]);
|
|
6
7
|
}
|
|
7
8
|
export declare class MediaDevices extends EventTarget {
|
|
8
|
-
readonly props:
|
|
9
|
-
video?: MediaStreamTrack;
|
|
10
|
-
audio?: MediaStreamTrack;
|
|
11
|
-
};
|
|
9
|
+
readonly props: MediaDeviceProps;
|
|
12
10
|
video?: MediaStreamTrack;
|
|
13
11
|
audio?: MediaStreamTrack;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
audio?: MediaStreamTrack;
|
|
17
|
-
});
|
|
12
|
+
private readonly activeSourceStops;
|
|
13
|
+
constructor(props?: MediaDeviceProps);
|
|
18
14
|
readonly getUserMedia: (constraints: MediaStreamConstraints) => Promise<MediaStream>;
|
|
19
15
|
readonly getDisplayMedia: (constraints: MediaStreamConstraints) => Promise<MediaStream>;
|
|
20
16
|
readonly getUdpMedia: ({ port, codec, }: {
|
|
@@ -24,6 +20,20 @@ export declare class MediaDevices extends EventTarget {
|
|
|
24
20
|
track: MediaStreamTrack;
|
|
25
21
|
disposer: () => void;
|
|
26
22
|
};
|
|
23
|
+
cleanup(): void;
|
|
24
|
+
private createTrack;
|
|
25
|
+
private createClonedTrack;
|
|
26
|
+
private createDummyTrack;
|
|
27
|
+
private attachTrackStop;
|
|
28
|
+
}
|
|
29
|
+
interface MediaDeviceProps {
|
|
30
|
+
video?: MediaStreamTrack;
|
|
31
|
+
audio?: MediaStreamTrack;
|
|
32
|
+
dummyMedia?: {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
audio?: Partial<DummyAudioOptions>;
|
|
35
|
+
video?: Partial<DummyVideoOptions>;
|
|
36
|
+
};
|
|
27
37
|
}
|
|
28
38
|
interface MediaStreamConstraints {
|
|
29
39
|
audio?: boolean | MediaTrackConstraints;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.navigator = exports.MediaDevices = exports.Navigator = void 0;
|
|
4
|
-
const crypto_1 = require("crypto");
|
|
5
4
|
const dgram_1 = require("dgram");
|
|
6
|
-
const jspack_1 = require("@shinyoshiaki/jspack");
|
|
7
5
|
const __1 = require("..");
|
|
8
6
|
const track_1 = require("../media/track");
|
|
7
|
+
const dummyMedia_1 = require("./dummyMedia");
|
|
9
8
|
class Navigator {
|
|
10
9
|
constructor(props = {}) {
|
|
11
10
|
Object.defineProperty(this, "mediaDevices", {
|
|
@@ -19,7 +18,7 @@ class Navigator {
|
|
|
19
18
|
}
|
|
20
19
|
exports.Navigator = Navigator;
|
|
21
20
|
class MediaDevices extends EventTarget {
|
|
22
|
-
constructor(props) {
|
|
21
|
+
constructor(props = {}) {
|
|
23
22
|
super();
|
|
24
23
|
Object.defineProperty(this, "props", {
|
|
25
24
|
enumerable: true,
|
|
@@ -39,41 +38,29 @@ class MediaDevices extends EventTarget {
|
|
|
39
38
|
writable: true,
|
|
40
39
|
value: void 0
|
|
41
40
|
});
|
|
41
|
+
Object.defineProperty(this, "activeSourceStops", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: new Map()
|
|
46
|
+
});
|
|
42
47
|
Object.defineProperty(this, "getUserMedia", {
|
|
43
48
|
enumerable: true,
|
|
44
49
|
configurable: true,
|
|
45
50
|
writable: true,
|
|
46
51
|
value: async (constraints) => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const audio = constraints.audio
|
|
58
|
-
? new track_1.MediaStreamTrack({ kind: "audio" })
|
|
59
|
-
: undefined;
|
|
60
|
-
if (audio) {
|
|
61
|
-
this.audio?.onReceiveRtp.subscribe((rtp) => {
|
|
62
|
-
const cloned = rtp.clone();
|
|
63
|
-
cloned.header.ssrc = jspack_1.jspack.Unpack("!L", (0, crypto_1.randomBytes)(4))[0];
|
|
64
|
-
audio.onReceiveRtp.execute(cloned);
|
|
65
|
-
});
|
|
52
|
+
const tracks = [
|
|
53
|
+
...(constraints.audio
|
|
54
|
+
? [this.createTrack("audio", constraints.audio)]
|
|
55
|
+
: []),
|
|
56
|
+
...(constraints.video
|
|
57
|
+
? [this.createTrack("video", constraints.video)]
|
|
58
|
+
: []),
|
|
59
|
+
];
|
|
60
|
+
if (tracks.length === 0) {
|
|
61
|
+
throw new Error("At least one audio or video track is required");
|
|
66
62
|
}
|
|
67
|
-
|
|
68
|
-
return new track_1.MediaStream([video, audio]);
|
|
69
|
-
}
|
|
70
|
-
else if (constraints.audio) {
|
|
71
|
-
return new track_1.MediaStream([audio]);
|
|
72
|
-
}
|
|
73
|
-
else if (constraints.video) {
|
|
74
|
-
return new track_1.MediaStream([video]);
|
|
75
|
-
}
|
|
76
|
-
throw new Error("Not implemented");
|
|
63
|
+
return new track_1.MediaStream(tracks);
|
|
77
64
|
}
|
|
78
65
|
});
|
|
79
66
|
Object.defineProperty(this, "getDisplayMedia", {
|
|
@@ -108,7 +95,84 @@ class MediaDevices extends EventTarget {
|
|
|
108
95
|
this.video = props.video;
|
|
109
96
|
this.audio = props.audio;
|
|
110
97
|
}
|
|
98
|
+
cleanup() {
|
|
99
|
+
for (const stop of this.activeSourceStops.values()) {
|
|
100
|
+
stop();
|
|
101
|
+
}
|
|
102
|
+
this.activeSourceStops.clear();
|
|
103
|
+
}
|
|
104
|
+
createTrack(kind, constraints) {
|
|
105
|
+
const existingTrack = kind === "audio" ? this.audio : this.video;
|
|
106
|
+
if (existingTrack) {
|
|
107
|
+
return this.createClonedTrack(kind, existingTrack);
|
|
108
|
+
}
|
|
109
|
+
if (this.props.dummyMedia?.enabled) {
|
|
110
|
+
return this.createDummyTrack(kind, constraints);
|
|
111
|
+
}
|
|
112
|
+
throw new Error(`No ${kind} source configured for getUserMedia`);
|
|
113
|
+
}
|
|
114
|
+
createClonedTrack(kind, sourceTrack) {
|
|
115
|
+
const track = new track_1.MediaStreamTrack({ kind });
|
|
116
|
+
const { unSubscribe } = sourceTrack.onReceiveRtp.subscribe((rtp) => {
|
|
117
|
+
track.onReceiveRtp.execute(rtp.clone());
|
|
118
|
+
});
|
|
119
|
+
this.attachTrackStop(track, () => {
|
|
120
|
+
unSubscribe();
|
|
121
|
+
});
|
|
122
|
+
return track;
|
|
123
|
+
}
|
|
124
|
+
createDummyTrack(kind, constraints) {
|
|
125
|
+
const dummy = kind === "audio"
|
|
126
|
+
? (0, dummyMedia_1.createDummyAudioTrack)(this.props.dummyMedia?.audio)
|
|
127
|
+
: (0, dummyMedia_1.createDummyVideoTrack)({
|
|
128
|
+
...this.props.dummyMedia?.video,
|
|
129
|
+
...resolveVideoOptions(constraints),
|
|
130
|
+
});
|
|
131
|
+
this.attachTrackStop(dummy.track, () => {
|
|
132
|
+
dummy.source.stop();
|
|
133
|
+
});
|
|
134
|
+
return dummy.track;
|
|
135
|
+
}
|
|
136
|
+
attachTrackStop(track, stop) {
|
|
137
|
+
let disposed = false;
|
|
138
|
+
const originalStop = track.stop;
|
|
139
|
+
const dispose = () => {
|
|
140
|
+
if (disposed) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
disposed = true;
|
|
144
|
+
stop();
|
|
145
|
+
this.activeSourceStops.delete(track);
|
|
146
|
+
};
|
|
147
|
+
track.stop = () => {
|
|
148
|
+
dispose();
|
|
149
|
+
originalStop();
|
|
150
|
+
};
|
|
151
|
+
this.activeSourceStops.set(track, dispose);
|
|
152
|
+
}
|
|
111
153
|
}
|
|
112
154
|
exports.MediaDevices = MediaDevices;
|
|
113
155
|
exports.navigator = new Navigator();
|
|
156
|
+
function resolveVideoOptions(constraints) {
|
|
157
|
+
if (typeof constraints === "boolean") {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
const fps = resolveConstrainNumber(constraints.frameRate);
|
|
161
|
+
if (!fps) {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
fps,
|
|
166
|
+
keyframeIntervalFrames: Math.max(1, Math.round(fps)),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function resolveConstrainNumber(value) {
|
|
170
|
+
if (typeof value === "number") {
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
if (!value || typeof value !== "object") {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
return value.exact ?? value.ideal ?? value.max ?? value.min;
|
|
177
|
+
}
|
|
114
178
|
//# sourceMappingURL=navigator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigator.js","sourceRoot":"","sources":["../../../../src/nonstandard/navigator.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"crypto\";\nimport { createSocket } from \"dgram\";\nimport { jspack } from \"@shinyoshiaki/jspack\";\nimport { RTCRtpCodecParameters } from \"..\";\nimport { MediaStream, MediaStreamTrack } from \"../media/track\";\n\nexport class Navigator {\n mediaDevices: MediaDevices;\n\n constructor(props: ConstructorParameters<typeof MediaDevices>[0] = {}) {\n this.mediaDevices = new MediaDevices(props);\n }\n}\n\nexport class MediaDevices extends EventTarget {\n video?: MediaStreamTrack;\n audio?: MediaStreamTrack;\n\n constructor(\n readonly props: { video?: MediaStreamTrack; audio?: MediaStreamTrack },\n ) {\n super();\n this.video = props.video;\n this.audio = props.audio;\n }\n\n readonly getUserMedia = async (\n constraints: MediaStreamConstraints,\n ): Promise<MediaStream> => {\n const video = constraints.video\n ? new MediaStreamTrack({ kind: \"video\" })\n : undefined;\n if (video) {\n this.video?.onReceiveRtp.subscribe((rtp) => {\n const cloned = rtp.clone();\n cloned.header.ssrc = jspack.Unpack(\"!L\", randomBytes(4))[0];\n video.onReceiveRtp.execute(cloned);\n });\n }\n const audio = constraints.audio\n ? new MediaStreamTrack({ kind: \"audio\" })\n : undefined;\n if (audio) {\n this.audio?.onReceiveRtp.subscribe((rtp) => {\n const cloned = rtp.clone();\n cloned.header.ssrc = jspack.Unpack(\"!L\", randomBytes(4))[0];\n audio.onReceiveRtp.execute(cloned);\n });\n }\n\n if (constraints.video && constraints.audio) {\n return new MediaStream([video!, audio!]);\n } else if (constraints.audio) {\n return new MediaStream([audio!]);\n } else if (constraints.video) {\n return new MediaStream([video!]);\n }\n\n throw new Error(\"Not implemented\");\n };\n\n readonly getDisplayMedia = this.getUserMedia;\n\n readonly getUdpMedia = ({\n port,\n codec,\n }: {\n port: number;\n codec: ConstructorParameters<typeof RTCRtpCodecParameters>[0];\n }) => {\n const kind = codec.mimeType.toLowerCase().includes(\"video\")\n ? \"video\"\n : \"audio\";\n const track = new MediaStreamTrack({\n kind,\n codec: new RTCRtpCodecParameters(codec),\n });\n\n const udp = createSocket(\"udp4\");\n udp.bind(port);\n udp.on(\"message\", (data) => {\n track.writeRtp(data);\n });\n\n const disposer = () => {\n udp.close();\n };\n\n return { track, disposer };\n };\n}\n\ninterface MediaStreamConstraints {\n audio?: boolean | MediaTrackConstraints;\n peerIdentity?: string;\n preferCurrentTab?: boolean;\n video?: boolean | MediaTrackConstraints;\n}\n\ninterface MediaTrackConstraints extends MediaTrackConstraintSet {\n advanced?: MediaTrackConstraintSet[];\n}\n\ninterface MediaTrackConstraintSet {\n aspectRatio?: ConstrainDouble;\n autoGainControl?: ConstrainBoolean;\n channelCount?: ConstrainULong;\n deviceId?: ConstrainDOMString;\n displaySurface?: ConstrainDOMString;\n echoCancellation?: ConstrainBoolean;\n facingMode?: ConstrainDOMString;\n frameRate?: ConstrainDouble;\n groupId?: ConstrainDOMString;\n height?: ConstrainULong;\n noiseSuppression?: ConstrainBoolean;\n sampleRate?: ConstrainULong;\n sampleSize?: ConstrainULong;\n width?: ConstrainULong;\n}\n\ntype ConstrainDOMString = string | string[] | ConstrainDOMStringParameters;\ninterface ConstrainDOMStringParameters {\n exact?: string | string[];\n ideal?: string | string[];\n}\ntype ConstrainBoolean = boolean | ConstrainBooleanParameters;\ninterface ConstrainBooleanParameters {\n exact?: boolean;\n ideal?: boolean;\n}\ntype ConstrainULong = number | ConstrainULongRange;\ninterface ConstrainULongRange extends ULongRange {\n exact?: number;\n ideal?: number;\n}\ninterface ULongRange {\n max?: number;\n min?: number;\n}\ntype ConstrainDouble = number | ConstrainDoubleRange;\ninterface ConstrainDoubleRange extends DoubleRange {\n exact?: number;\n ideal?: number;\n}\ninterface DoubleRange {\n max?: number;\n min?: number;\n}\n\nexport const navigator = new Navigator();\n"]}
|
|
1
|
+
{"version":3,"file":"navigator.js","sourceRoot":"","sources":["../../../../src/nonstandard/navigator.ts"],"names":[],"mappings":"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","sourcesContent":["import { createSocket } from \"dgram\";\nimport { RTCRtpCodecParameters } from \"..\";\nimport { MediaStream, MediaStreamTrack } from \"../media/track\";\nimport {\n type DummyAudioOptions,\n type DummyVideoOptions,\n createDummyAudioTrack,\n createDummyVideoTrack,\n} from \"./dummyMedia\";\n\nexport class Navigator {\n mediaDevices: MediaDevices;\n\n constructor(props: ConstructorParameters<typeof MediaDevices>[0] = {}) {\n this.mediaDevices = new MediaDevices(props);\n }\n}\n\nexport class MediaDevices extends EventTarget {\n video?: MediaStreamTrack;\n audio?: MediaStreamTrack;\n private readonly activeSourceStops = new Map<MediaStreamTrack, () => void>();\n\n constructor(readonly props: MediaDeviceProps = {}) {\n super();\n this.video = props.video;\n this.audio = props.audio;\n }\n\n readonly getUserMedia = async (\n constraints: MediaStreamConstraints,\n ): Promise<MediaStream> => {\n const tracks = [\n ...(constraints.audio\n ? [this.createTrack(\"audio\", constraints.audio)]\n : []),\n ...(constraints.video\n ? [this.createTrack(\"video\", constraints.video)]\n : []),\n ];\n\n if (tracks.length === 0) {\n throw new Error(\"At least one audio or video track is required\");\n }\n\n return new MediaStream(tracks);\n };\n\n readonly getDisplayMedia = this.getUserMedia;\n\n readonly getUdpMedia = ({\n port,\n codec,\n }: {\n port: number;\n codec: ConstructorParameters<typeof RTCRtpCodecParameters>[0];\n }) => {\n const kind = codec.mimeType.toLowerCase().includes(\"video\")\n ? \"video\"\n : \"audio\";\n const track = new MediaStreamTrack({\n kind,\n codec: new RTCRtpCodecParameters(codec),\n });\n\n const udp = createSocket(\"udp4\");\n udp.bind(port);\n udp.on(\"message\", (data) => {\n track.writeRtp(data);\n });\n\n const disposer = () => {\n udp.close();\n };\n\n return { track, disposer };\n };\n\n cleanup() {\n for (const stop of this.activeSourceStops.values()) {\n stop();\n }\n this.activeSourceStops.clear();\n }\n\n private createTrack(\n kind: \"audio\" | \"video\",\n constraints: boolean | MediaTrackConstraints,\n ) {\n const existingTrack = kind === \"audio\" ? this.audio : this.video;\n if (existingTrack) {\n return this.createClonedTrack(kind, existingTrack);\n }\n if (this.props.dummyMedia?.enabled) {\n return this.createDummyTrack(kind, constraints);\n }\n throw new Error(`No ${kind} source configured for getUserMedia`);\n }\n\n private createClonedTrack(\n kind: \"audio\" | \"video\",\n sourceTrack: MediaStreamTrack,\n ) {\n const track = new MediaStreamTrack({ kind });\n const { unSubscribe } = sourceTrack.onReceiveRtp.subscribe((rtp) => {\n track.onReceiveRtp.execute(rtp.clone());\n });\n this.attachTrackStop(track, () => {\n unSubscribe();\n });\n return track;\n }\n\n private createDummyTrack(\n kind: \"audio\" | \"video\",\n constraints: boolean | MediaTrackConstraints,\n ) {\n const dummy =\n kind === \"audio\"\n ? createDummyAudioTrack(this.props.dummyMedia?.audio)\n : createDummyVideoTrack({\n ...this.props.dummyMedia?.video,\n ...resolveVideoOptions(constraints),\n });\n\n this.attachTrackStop(dummy.track, () => {\n dummy.source.stop();\n });\n\n return dummy.track;\n }\n\n private attachTrackStop(track: MediaStreamTrack, stop: () => void) {\n let disposed = false;\n const originalStop = track.stop;\n const dispose = () => {\n if (disposed) {\n return;\n }\n disposed = true;\n stop();\n this.activeSourceStops.delete(track);\n };\n\n track.stop = () => {\n dispose();\n originalStop();\n };\n\n this.activeSourceStops.set(track, dispose);\n }\n}\n\ninterface MediaDeviceProps {\n video?: MediaStreamTrack;\n audio?: MediaStreamTrack;\n dummyMedia?: {\n enabled: boolean;\n audio?: Partial<DummyAudioOptions>;\n video?: Partial<DummyVideoOptions>;\n };\n}\n\ninterface MediaStreamConstraints {\n audio?: boolean | MediaTrackConstraints;\n peerIdentity?: string;\n preferCurrentTab?: boolean;\n video?: boolean | MediaTrackConstraints;\n}\n\ninterface MediaTrackConstraints extends MediaTrackConstraintSet {\n advanced?: MediaTrackConstraintSet[];\n}\n\ninterface MediaTrackConstraintSet {\n aspectRatio?: ConstrainDouble;\n autoGainControl?: ConstrainBoolean;\n channelCount?: ConstrainULong;\n deviceId?: ConstrainDOMString;\n displaySurface?: ConstrainDOMString;\n echoCancellation?: ConstrainBoolean;\n facingMode?: ConstrainDOMString;\n frameRate?: ConstrainDouble;\n groupId?: ConstrainDOMString;\n height?: ConstrainULong;\n noiseSuppression?: ConstrainBoolean;\n sampleRate?: ConstrainULong;\n sampleSize?: ConstrainULong;\n width?: ConstrainULong;\n}\n\ntype ConstrainDOMString = string | string[] | ConstrainDOMStringParameters;\ninterface ConstrainDOMStringParameters {\n exact?: string | string[];\n ideal?: string | string[];\n}\ntype ConstrainBoolean = boolean | ConstrainBooleanParameters;\ninterface ConstrainBooleanParameters {\n exact?: boolean;\n ideal?: boolean;\n}\ntype ConstrainULong = number | ConstrainULongRange;\ninterface ConstrainULongRange extends ULongRange {\n exact?: number;\n ideal?: number;\n}\ninterface ULongRange {\n max?: number;\n min?: number;\n}\ntype ConstrainDouble = number | ConstrainDoubleRange;\ninterface ConstrainDoubleRange extends DoubleRange {\n exact?: number;\n ideal?: number;\n}\ninterface DoubleRange {\n max?: number;\n min?: number;\n}\n\nexport const navigator = new Navigator();\n\nfunction resolveVideoOptions(\n constraints: boolean | MediaTrackConstraints,\n): Partial<DummyVideoOptions> {\n if (typeof constraints === \"boolean\") {\n return {};\n }\n\n const fps = resolveConstrainNumber(constraints.frameRate);\n if (!fps) {\n return {};\n }\n\n return {\n fps,\n keyframeIntervalFrames: Math.max(1, Math.round(fps)),\n };\n}\n\nfunction resolveConstrainNumber(\n value?: ConstrainDouble | ConstrainULong,\n): number | undefined {\n if (typeof value === \"number\") {\n return value;\n }\n if (!value || typeof value !== \"object\") {\n return;\n }\n return value.exact ?? value.ideal ?? value.max ?? value.min;\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RtpPacket } from "../../imports/rtp";
|
|
2
|
+
import type { RTCRtpCodecParameters } from "../../media/parameters";
|
|
3
|
+
import type { EncodedPacket } from "mediabunny";
|
|
4
|
+
export declare const supportedSourceCodecs: readonly ["avc", "vp8", "vp9", "av1", "opus"];
|
|
5
|
+
export type SupportedSourceCodec = (typeof supportedSourceCodecs)[number];
|
|
6
|
+
type SupportedMimeType = "video/h264" | "video/vp8" | "video/vp9" | "video/av1x" | "audio/opus";
|
|
7
|
+
export interface Packetizer {
|
|
8
|
+
packetize(packet: EncodedPacket, rtpTimestamp: number): RtpPacket[];
|
|
9
|
+
}
|
|
10
|
+
export declare function toSupportedMimeType(codec: SupportedSourceCodec): SupportedMimeType;
|
|
11
|
+
export declare function createPacketizer({ codec, sourceCodec, decoderDescription, }: {
|
|
12
|
+
codec: RTCRtpCodecParameters;
|
|
13
|
+
sourceCodec: SupportedSourceCodec;
|
|
14
|
+
decoderDescription?: ArrayBuffer | ArrayBufferView | null;
|
|
15
|
+
}): Packetizer;
|
|
16
|
+
export {};
|