werift 0.20.0 → 0.21.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/lib/common/src/event.d.ts +45 -0
- package/lib/common/src/event.js +229 -0
- package/lib/common/src/event.js.map +1 -0
- package/lib/common/src/index.d.ts +1 -0
- package/lib/common/src/index.js +1 -0
- package/lib/common/src/index.js.map +1 -1
- package/lib/dtls/src/flight/client/flight5.js +6 -1
- package/lib/dtls/src/flight/client/flight5.js.map +1 -1
- package/lib/dtls/src/flight/server/flight2.js +5 -3
- package/lib/dtls/src/flight/server/flight2.js.map +1 -1
- package/lib/dtls/src/imports/common.d.ts +1 -0
- package/lib/dtls/src/imports/common.js +18 -0
- package/lib/dtls/src/imports/common.js.map +1 -0
- package/lib/dtls/src/index.d.ts +1 -1
- package/lib/dtls/src/index.js.map +1 -1
- package/lib/dtls/src/record/receive.js +1 -1
- package/lib/dtls/src/record/receive.js.map +1 -1
- package/lib/dtls/src/socket.d.ts +1 -1
- package/lib/dtls/src/socket.js +5 -5
- package/lib/dtls/src/socket.js.map +1 -1
- package/lib/ice/src/candidate.d.ts +3 -2
- package/lib/ice/src/candidate.js +19 -5
- package/lib/ice/src/candidate.js.map +1 -1
- package/lib/ice/src/helper.d.ts +6 -6
- package/lib/ice/src/helper.js +20 -17
- package/lib/ice/src/helper.js.map +1 -1
- package/lib/ice/src/ice.d.ts +29 -71
- package/lib/ice/src/ice.js +417 -417
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +109 -0
- package/lib/ice/src/iceBase.js +166 -0
- package/lib/ice/src/iceBase.js.map +1 -0
- package/lib/ice/src/imports/common.d.ts +1 -0
- package/lib/ice/src/imports/common.js +18 -0
- package/lib/ice/src/imports/common.js.map +1 -0
- package/lib/ice/src/index.d.ts +5 -0
- package/lib/ice/src/index.js +5 -0
- package/lib/ice/src/index.js.map +1 -1
- package/lib/ice/src/stun/message.d.ts +7 -0
- package/lib/ice/src/stun/message.js +20 -6
- package/lib/ice/src/stun/message.js.map +1 -1
- package/lib/ice/src/stun/protocol.d.ts +6 -6
- package/lib/ice/src/stun/protocol.js +29 -23
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/transaction.js +10 -4
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/transport.d.ts +21 -4
- package/lib/ice/src/transport.js +115 -7
- package/lib/ice/src/transport.js.map +1 -1
- package/lib/ice/src/turn/protocol.d.ts +54 -37
- package/lib/ice/src/turn/protocol.js +219 -94
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +9 -5
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/ice/src/utils.d.ts +1 -0
- package/lib/ice/src/utils.js +15 -16
- package/lib/ice/src/utils.js.map +1 -1
- package/lib/rtp/src/extra/container/mp4/container.d.ts +1 -1
- package/lib/rtp/src/extra/container/mp4/container.js +2 -5
- package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
- package/lib/rtp/src/extra/container/webm/container.d.ts +3 -1
- package/lib/rtp/src/extra/container/webm/container.js +8 -2
- package/lib/rtp/src/extra/container/webm/container.js.map +1 -1
- package/lib/rtp/src/extra/container/webm/ebml/id.d.ts +6 -0
- package/lib/rtp/src/extra/container/webm/ebml/id.js +6 -0
- package/lib/rtp/src/extra/container/webm/ebml/id.js.map +1 -1
- package/lib/rtp/src/extra/processor/depacketizer.d.ts +1 -1
- package/lib/rtp/src/extra/processor/depacketizer.js +2 -2
- package/lib/rtp/src/extra/processor/depacketizer.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4.d.ts +1 -1
- package/lib/rtp/src/extra/processor/mp4.js +2 -5
- package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
- package/lib/rtp/src/extra/processor/nack.d.ts +1 -1
- package/lib/rtp/src/extra/processor/nack.js +3 -3
- package/lib/rtp/src/extra/processor/nack.js.map +1 -1
- package/lib/rtp/src/extra/processor/rtcpCallback.d.ts +1 -1
- package/lib/rtp/src/extra/processor/rtcpCallback.js +2 -5
- package/lib/rtp/src/extra/processor/rtcpCallback.js.map +1 -1
- package/lib/rtp/src/extra/processor/rtpCallback.d.ts +1 -1
- package/lib/rtp/src/extra/processor/rtpCallback.js +2 -5
- package/lib/rtp/src/extra/processor/rtpCallback.js.map +1 -1
- package/lib/rtp/src/extra/processor/webm.d.ts +12 -17
- package/lib/rtp/src/extra/processor/webm.js +2 -5
- package/lib/rtp/src/extra/processor/webm.js.map +1 -1
- package/lib/rtp/src/extra/processor/webmCallback.d.ts +1 -10
- package/lib/rtp/src/extra/processor/webmCallback.js.map +1 -1
- package/lib/rtp/src/imports/common.d.ts +1 -0
- package/lib/rtp/src/imports/common.js +18 -0
- package/lib/rtp/src/imports/common.js.map +1 -0
- package/lib/rtp/src/rtp/headerExtension.d.ts +45 -3
- package/lib/rtp/src/rtp/headerExtension.js +15 -0
- package/lib/rtp/src/rtp/headerExtension.js.map +1 -1
- package/lib/sctp/src/helper.js +2 -5
- package/lib/sctp/src/helper.js.map +1 -1
- package/lib/sctp/src/imports/common.d.ts +1 -0
- package/lib/sctp/src/imports/common.js +18 -0
- package/lib/sctp/src/imports/common.js.map +1 -0
- package/lib/sctp/src/index.d.ts +1 -1
- package/lib/sctp/src/index.js.map +1 -1
- package/lib/sctp/src/sctp.d.ts +1 -1
- package/lib/sctp/src/sctp.js +3 -3
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/const.d.ts +2 -2
- package/lib/webrtc/src/const.js.map +1 -1
- package/lib/webrtc/src/dataChannel.d.ts +1 -1
- package/lib/webrtc/src/dataChannel.js +5 -5
- package/lib/webrtc/src/dataChannel.js.map +1 -1
- package/lib/webrtc/src/imports/common.d.ts +1 -0
- package/lib/webrtc/src/imports/common.js +18 -0
- package/lib/webrtc/src/imports/common.js.map +1 -0
- package/lib/webrtc/src/media/extension/rtpExtension.d.ts +1 -0
- package/lib/webrtc/src/media/extension/rtpExtension.js +6 -0
- package/lib/webrtc/src/media/extension/rtpExtension.js.map +1 -1
- package/lib/webrtc/src/media/receiver/nack.d.ts +1 -1
- package/lib/webrtc/src/media/receiver/nack.js +2 -2
- package/lib/webrtc/src/media/receiver/nack.js.map +1 -1
- package/lib/webrtc/src/media/router.d.ts +0 -3
- package/lib/webrtc/src/media/router.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +3 -4
- package/lib/webrtc/src/media/rtpReceiver.js +4 -7
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +1 -1
- package/lib/webrtc/src/media/rtpSender.js +5 -5
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.js +2 -5
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/sender/senderBWE.d.ts +1 -1
- package/lib/webrtc/src/media/sender/senderBWE.js +4 -7
- package/lib/webrtc/src/media/sender/senderBWE.js.map +1 -1
- package/lib/webrtc/src/media/track.d.ts +3 -3
- package/lib/webrtc/src/media/track.js +4 -7
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/index.d.ts +3 -1
- package/lib/webrtc/src/nonstandard/recorder/index.js +2 -5
- package/lib/webrtc/src/nonstandard/recorder/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/index.d.ts +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.d.ts +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +3 -2
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +15 -8
- package/lib/webrtc/src/peerConnection.js +107 -93
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sdp.js +15 -3
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +6 -6
- package/lib/webrtc/src/transport/dtls.js +19 -26
- package/lib/webrtc/src/transport/dtls.js.map +1 -1
- package/lib/webrtc/src/transport/ice.d.ts +22 -11
- package/lib/webrtc/src/transport/ice.js +106 -38
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +1 -1
- package/lib/webrtc/src/transport/sctp.js +2 -2
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/package.json +1 -1
- package/src/const.ts +2 -2
- package/src/dataChannel.ts +1 -1
- package/src/imports/common.ts +1 -0
- package/src/media/extension/rtpExtension.ts +6 -0
- package/src/media/receiver/nack.ts +1 -1
- package/src/media/router.ts +2 -3
- package/src/media/rtpReceiver.ts +5 -4
- package/src/media/rtpSender.ts +1 -1
- package/src/media/rtpTransceiver.ts +1 -1
- package/src/media/sender/senderBWE.ts +1 -1
- package/src/media/track.ts +9 -4
- package/src/nonstandard/recorder/index.ts +4 -1
- package/src/nonstandard/recorder/writer/index.ts +1 -1
- package/src/nonstandard/recorder/writer/webm.ts +48 -44
- package/src/peerConnection.ts +122 -89
- package/src/sdp.ts +16 -3
- package/src/transport/dtls.ts +22 -14
- package/src/transport/ice.ts +83 -31
- package/src/transport/sctp.ts +1 -1
package/src/media/track.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import Event from "rx.mini";
|
|
2
1
|
import { v4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { Event } from "../imports/common";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type Extensions,
|
|
6
|
+
type RtcpPacket,
|
|
7
|
+
type RtpHeader,
|
|
8
|
+
RtpPacket,
|
|
9
|
+
} from "../../../rtp/src";
|
|
5
10
|
import { EventTarget } from "../helper";
|
|
6
11
|
import type { Kind } from "../types/domain";
|
|
7
12
|
import type { RTCRtpCodecParameters } from "./parameters";
|
|
@@ -22,7 +27,7 @@ export class MediaStreamTrack extends EventTarget {
|
|
|
22
27
|
/**todo impl */
|
|
23
28
|
enabled = true;
|
|
24
29
|
|
|
25
|
-
readonly onReceiveRtp = new Event<[RtpPacket]>();
|
|
30
|
+
readonly onReceiveRtp = new Event<[RtpPacket, Extensions?]>();
|
|
26
31
|
readonly onReceiveRtcp = new Event<[RtcpPacket]>();
|
|
27
32
|
readonly onSourceChanged = new Event<
|
|
28
33
|
[Pick<RtpHeader, "sequenceNumber" | "timestamp">]
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import Event from "
|
|
1
|
+
import { Event } from "../../imports/common";
|
|
2
2
|
import type { MediaStreamTrack } from "../../media/track";
|
|
3
3
|
import type { MediaWriter, StreamEvent } from "./writer";
|
|
4
4
|
import { WebmFactory } from "./writer/webm";
|
|
5
5
|
|
|
6
|
+
export type { StreamEvent };
|
|
7
|
+
|
|
6
8
|
export class MediaRecorder {
|
|
7
9
|
writer: MediaWriter;
|
|
8
10
|
ext?: string;
|
|
@@ -90,6 +92,7 @@ export class MediaRecorder {
|
|
|
90
92
|
export interface MediaRecorderOptions {
|
|
91
93
|
width: number;
|
|
92
94
|
height: number;
|
|
95
|
+
roll: number;
|
|
93
96
|
jitterBufferLatency: number;
|
|
94
97
|
jitterBufferSize: number;
|
|
95
98
|
disableLipSync: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Event } from "rx.mini";
|
|
2
1
|
import type { MediaRecorderOptions } from "..";
|
|
3
2
|
import type { MediaStreamTrack } from "../../..";
|
|
4
3
|
import type { WebmOutput } from "../../../../../rtp/src/extra";
|
|
4
|
+
import type { Event } from "../../../imports/common";
|
|
5
5
|
|
|
6
6
|
export abstract class MediaWriter {
|
|
7
7
|
constructor(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { unlink } from "fs/promises";
|
|
2
|
-
import { EventDisposer } from "
|
|
2
|
+
import { EventDisposer } from "../../../imports/common";
|
|
3
3
|
|
|
4
4
|
import { MediaWriter } from ".";
|
|
5
5
|
import { type MediaStreamTrack, WeriftError } from "../../..";
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
RtpTimeCallback,
|
|
14
14
|
type SupportedCodec,
|
|
15
15
|
WebmCallback,
|
|
16
|
+
type WebmTrack,
|
|
16
17
|
saveToFileSystem,
|
|
17
18
|
} from "../../../../../rtp/src/extra";
|
|
18
19
|
|
|
@@ -28,49 +29,52 @@ export class WebmFactory extends MediaWriter {
|
|
|
28
29
|
await unlink(this.props.path).catch((e) => e);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
const inputTracks = tracks.map(
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
32
|
+
const inputTracks: (WebmTrack & { track: MediaStreamTrack })[] = tracks.map(
|
|
33
|
+
(track, i) => {
|
|
34
|
+
const trackNumber = i + 1;
|
|
35
|
+
const payloadType = track.codec!.payloadType;
|
|
36
|
+
|
|
37
|
+
if (track.kind === "video") {
|
|
38
|
+
const codec = ((): SupportedCodec => {
|
|
39
|
+
switch (track.codec?.name.toLowerCase() as SupportedVideoCodec) {
|
|
40
|
+
case "vp8":
|
|
41
|
+
return "VP8";
|
|
42
|
+
case "vp9":
|
|
43
|
+
return "VP9";
|
|
44
|
+
case "h264":
|
|
45
|
+
return "MPEG4/ISO/AVC";
|
|
46
|
+
case "av1x":
|
|
47
|
+
return "AV1";
|
|
48
|
+
default:
|
|
49
|
+
throw new WeriftError({
|
|
50
|
+
message: "unsupported codec",
|
|
51
|
+
payload: { track, path: sourcePath },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
return {
|
|
56
|
+
kind: "video" as const,
|
|
57
|
+
codec,
|
|
58
|
+
clockRate: 90000,
|
|
59
|
+
trackNumber,
|
|
60
|
+
width: this.props.width ?? 640,
|
|
61
|
+
height: this.props.height ?? 360,
|
|
62
|
+
roll: this.props.roll,
|
|
63
|
+
payloadType,
|
|
64
|
+
track,
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
return {
|
|
68
|
+
kind: "audio" as const,
|
|
69
|
+
codec: "OPUS" as const,
|
|
70
|
+
clockRate: 48000,
|
|
71
|
+
trackNumber,
|
|
72
|
+
payloadType,
|
|
73
|
+
track,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
);
|
|
74
78
|
|
|
75
79
|
const webm = new WebmCallback(inputTracks, {
|
|
76
80
|
duration: this.props.defaultDuration ?? 1000 * 60 * 60 * 24,
|
package/src/peerConnection.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
2
|
import cloneDeep from "lodash/cloneDeep";
|
|
3
3
|
import isEqual from "lodash/isEqual";
|
|
4
|
-
import Event from "rx.mini";
|
|
5
4
|
import * as uuid from "uuid";
|
|
5
|
+
import { Event } from "./imports/common";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
type Address,
|
|
@@ -81,7 +81,6 @@ const log = debug("werift:packages/webrtc/src/peerConnection.ts");
|
|
|
81
81
|
export class RTCPeerConnection extends EventTarget {
|
|
82
82
|
readonly cname = uuid.v4();
|
|
83
83
|
sctpTransport?: RTCSctpTransport;
|
|
84
|
-
transportEstablished = false;
|
|
85
84
|
config: Required<PeerConfig> = cloneDeep<PeerConfig>(defaultPeerConfig);
|
|
86
85
|
connectionState: ConnectionState = "new";
|
|
87
86
|
iceConnectionState: RTCIceConnectionState = "new";
|
|
@@ -95,8 +94,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
95
94
|
private replaceTransceiver(t: RTCRtpTransceiver, index: number) {
|
|
96
95
|
this.transceivers[index] = t;
|
|
97
96
|
}
|
|
98
|
-
|
|
99
|
-
candidatesSent = new Set<string>();
|
|
97
|
+
needRestart = false;
|
|
100
98
|
|
|
101
99
|
readonly iceGatheringStateChange = new Event<[IceGathererState]>();
|
|
102
100
|
readonly iceConnectionStateChange = new Event<[RTCIceConnectionState]>();
|
|
@@ -117,7 +115,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
117
115
|
onconnectionstatechange?: Callback;
|
|
118
116
|
|
|
119
117
|
private readonly router = new RtpRouter();
|
|
120
|
-
private
|
|
118
|
+
private certificate?: RTCCertificate;
|
|
121
119
|
sctpRemotePort?: number;
|
|
122
120
|
private seenMid = new Set<string>();
|
|
123
121
|
private currentLocalDescription?: SessionDescription;
|
|
@@ -147,9 +145,28 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
147
145
|
return this.router.extIdUriMap;
|
|
148
146
|
}
|
|
149
147
|
|
|
148
|
+
get iceGeneration() {
|
|
149
|
+
return this.iceTransports[0].connection.generation;
|
|
150
|
+
}
|
|
151
|
+
|
|
150
152
|
constructor(config: Partial<PeerConfig> = {}) {
|
|
151
153
|
super();
|
|
152
154
|
|
|
155
|
+
this.setConfiguration(config);
|
|
156
|
+
|
|
157
|
+
this.iceConnectionStateChange.subscribe((state) => {
|
|
158
|
+
switch (state) {
|
|
159
|
+
case "disconnected":
|
|
160
|
+
this.setConnectionState("disconnected");
|
|
161
|
+
break;
|
|
162
|
+
case "closed":
|
|
163
|
+
this.close();
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setConfiguration(config: Partial<PeerConfig>) {
|
|
153
170
|
deepMerge(this.config, config);
|
|
154
171
|
|
|
155
172
|
if (this.config.icePortRange) {
|
|
@@ -194,23 +211,15 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
194
211
|
|
|
195
212
|
if (this.config.dtls) {
|
|
196
213
|
const { keys } = this.config.dtls;
|
|
214
|
+
|
|
197
215
|
if (keys) {
|
|
198
|
-
this.
|
|
199
|
-
|
|
216
|
+
this.certificate = new RTCCertificate(
|
|
217
|
+
keys.keyPem,
|
|
218
|
+
keys.certPem,
|
|
219
|
+
keys.signatureHash,
|
|
200
220
|
);
|
|
201
221
|
}
|
|
202
222
|
}
|
|
203
|
-
|
|
204
|
-
this.iceConnectionStateChange.subscribe((state) => {
|
|
205
|
-
switch (state) {
|
|
206
|
-
case "disconnected":
|
|
207
|
-
this.setConnectionState("disconnected");
|
|
208
|
-
break;
|
|
209
|
-
case "closed":
|
|
210
|
-
this.close();
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
get localDescription() {
|
|
@@ -251,8 +260,16 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
251
260
|
return this.config;
|
|
252
261
|
}
|
|
253
262
|
|
|
254
|
-
async createOffer() {
|
|
263
|
+
async createOffer({ iceRestart }: { iceRestart?: boolean } = {}) {
|
|
264
|
+
if (iceRestart || this.needRestart) {
|
|
265
|
+
this.needRestart = false;
|
|
266
|
+
for (const t of this.iceTransports) {
|
|
267
|
+
t.restart();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
255
271
|
await this.ensureCerts();
|
|
272
|
+
|
|
256
273
|
const description = this.buildOfferSdp();
|
|
257
274
|
return description.toJSON();
|
|
258
275
|
}
|
|
@@ -465,7 +482,9 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
465
482
|
|
|
466
483
|
private needNegotiation = async () => {
|
|
467
484
|
this.shouldNegotiationneeded = true;
|
|
468
|
-
if (this.negotiationneeded || this.signalingState !== "stable")
|
|
485
|
+
if (this.negotiationneeded || this.signalingState !== "stable") {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
469
488
|
this.shouldNegotiationneeded = false;
|
|
470
489
|
setImmediate(() => {
|
|
471
490
|
this.negotiationneeded = true;
|
|
@@ -493,11 +512,13 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
493
512
|
additionalHostAddresses: this.config.iceAdditionalHostAddresses,
|
|
494
513
|
filterStunResponse: this.config.iceFilterStunResponse,
|
|
495
514
|
filterCandidatePair: this.config.iceFilterCandidatePair,
|
|
515
|
+
localPasswordPrefix: this.config.icePasswordPrefix,
|
|
496
516
|
useIpv4: this.config.iceUseIpv4,
|
|
497
517
|
useIpv6: this.config.iceUseIpv6,
|
|
518
|
+
turnTransport: this.config.forceTurnTCP === true ? "tcp" : "udp",
|
|
498
519
|
});
|
|
499
520
|
if (existing) {
|
|
500
|
-
iceGatherer.connection.
|
|
521
|
+
iceGatherer.connection.localUsername = existing.connection.localUsername;
|
|
501
522
|
iceGatherer.connection.localPassword = existing.connection.localPassword;
|
|
502
523
|
}
|
|
503
524
|
iceGatherer.onGatheringStateChange.subscribe(() => {
|
|
@@ -508,8 +529,10 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
508
529
|
iceTransport.onStateChange.subscribe(() => {
|
|
509
530
|
this.updateIceConnectionState();
|
|
510
531
|
});
|
|
511
|
-
|
|
512
|
-
|
|
532
|
+
iceTransport.onNegotiationNeeded.subscribe(() => {
|
|
533
|
+
this.needNegotiation();
|
|
534
|
+
});
|
|
535
|
+
iceTransport.onIceCandidate.subscribe((candidate) => {
|
|
513
536
|
if (!this.localDescription) {
|
|
514
537
|
log("localDescription not found when ice candidate was gathered");
|
|
515
538
|
return;
|
|
@@ -548,28 +571,18 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
548
571
|
|
|
549
572
|
candidate.foundation = "candidate:" + candidate.foundation;
|
|
550
573
|
|
|
551
|
-
// prevent ice candidates that have already been sent from being being resent
|
|
552
|
-
// when the connection is renegotiated during a later setLocalDescription call.
|
|
553
|
-
if (candidate.sdpMid) {
|
|
554
|
-
const candidateKey = `${candidate.foundation}:${candidate.sdpMid}`;
|
|
555
|
-
if (this.candidatesSent.has(candidateKey)) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
this.candidatesSent.add(candidateKey);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
574
|
this.onIceCandidate.execute(candidate.toJSON());
|
|
562
575
|
if (this.onicecandidate) {
|
|
563
576
|
this.onicecandidate({ candidate: candidate.toJSON() });
|
|
564
577
|
}
|
|
565
578
|
this.emit("icecandidate", { candidate });
|
|
566
|
-
};
|
|
579
|
+
});
|
|
567
580
|
|
|
568
581
|
const dtlsTransport = new RTCDtlsTransport(
|
|
569
582
|
this.config,
|
|
570
583
|
iceTransport,
|
|
571
584
|
this.router,
|
|
572
|
-
this.
|
|
585
|
+
this.certificate,
|
|
573
586
|
srtpProfiles,
|
|
574
587
|
);
|
|
575
588
|
|
|
@@ -616,7 +629,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
616
629
|
}
|
|
617
630
|
|
|
618
631
|
// # assign MID
|
|
619
|
-
description.media
|
|
632
|
+
for (const [i, media] of enumerate(description.media)) {
|
|
620
633
|
const mid = media.rtp.muxId!;
|
|
621
634
|
this.seenMid.add(mid);
|
|
622
635
|
if (["audio", "video"].includes(media.kind)) {
|
|
@@ -628,9 +641,10 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
628
641
|
if (media.kind === "application" && this.sctpTransport) {
|
|
629
642
|
this.sctpTransport.mid = mid;
|
|
630
643
|
}
|
|
631
|
-
}
|
|
644
|
+
}
|
|
632
645
|
|
|
633
|
-
|
|
646
|
+
// setup ice,dtls role
|
|
647
|
+
for (const dtlsTransport of this.dtlsTransports) {
|
|
634
648
|
const iceTransport = dtlsTransport.iceTransport;
|
|
635
649
|
|
|
636
650
|
// # set ICE role
|
|
@@ -653,34 +667,22 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
653
667
|
dtlsTransport.role = role;
|
|
654
668
|
}
|
|
655
669
|
}
|
|
656
|
-
}
|
|
657
|
-
this.dtlsTransports.forEach((d) => setupRole(d));
|
|
670
|
+
}
|
|
658
671
|
|
|
659
672
|
// # configure direction
|
|
660
673
|
if (["answer", "pranswer"].includes(description.type)) {
|
|
661
|
-
this.transceivers
|
|
674
|
+
for (const t of this.transceivers) {
|
|
662
675
|
const direction = andDirection(t.direction, t.offerDirection);
|
|
663
676
|
t.setCurrentDirection(direction);
|
|
664
|
-
}
|
|
677
|
+
}
|
|
665
678
|
}
|
|
666
679
|
|
|
667
680
|
// for trickle ice
|
|
668
681
|
this.setLocal(description);
|
|
669
682
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
);
|
|
674
|
-
if (this.remoteIsBundled && connected) {
|
|
675
|
-
// no need to gather ice candidates on an existing bundled connection
|
|
676
|
-
await connected.iceGather.gather();
|
|
677
|
-
} else {
|
|
678
|
-
await Promise.allSettled(
|
|
679
|
-
this.iceTransports.map((iceTransport) =>
|
|
680
|
-
iceTransport.iceGather.gather(),
|
|
681
|
-
),
|
|
682
|
-
);
|
|
683
|
-
}
|
|
683
|
+
await this.gatherCandidates().catch((e) => {
|
|
684
|
+
log("gatherCandidates failed", e);
|
|
685
|
+
});
|
|
684
686
|
|
|
685
687
|
// connect transports
|
|
686
688
|
if (description.type === "answer") {
|
|
@@ -699,6 +701,20 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
699
701
|
return description;
|
|
700
702
|
}
|
|
701
703
|
|
|
704
|
+
private async gatherCandidates() {
|
|
705
|
+
// # gather candidates
|
|
706
|
+
const connected = this.iceTransports.find(
|
|
707
|
+
(transport) => transport.state === "connected",
|
|
708
|
+
);
|
|
709
|
+
if (this.remoteIsBundled && connected) {
|
|
710
|
+
// no need to gather ice candidates on an existing bundled connection
|
|
711
|
+
} else {
|
|
712
|
+
await Promise.allSettled(
|
|
713
|
+
this.iceTransports.map((iceTransport) => iceTransport.gather()),
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
702
718
|
private setLocal(description: SessionDescription) {
|
|
703
719
|
description.media
|
|
704
720
|
.filter((m) => ["audio", "video"].includes(m.kind))
|
|
@@ -770,20 +786,25 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
770
786
|
}
|
|
771
787
|
|
|
772
788
|
private async connect() {
|
|
773
|
-
if (this.transportEstablished) {
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
789
|
log("start connect");
|
|
777
790
|
|
|
778
|
-
this.setConnectionState("connecting");
|
|
779
|
-
|
|
780
791
|
await Promise.allSettled(
|
|
781
792
|
this.dtlsTransports.map(async (dtlsTransport) => {
|
|
782
793
|
const { iceTransport } = dtlsTransport;
|
|
794
|
+
if (iceTransport.state === "connected") {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
this.setConnectionState("connecting");
|
|
799
|
+
|
|
783
800
|
await iceTransport.start().catch((err) => {
|
|
784
801
|
log("iceTransport.start failed", err);
|
|
785
802
|
throw err;
|
|
786
803
|
});
|
|
804
|
+
|
|
805
|
+
if (dtlsTransport.state === "connected") {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
787
808
|
await dtlsTransport.start().catch((err) => {
|
|
788
809
|
log("dtlsTransport.start failed", err);
|
|
789
810
|
throw err;
|
|
@@ -800,7 +821,6 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
800
821
|
}),
|
|
801
822
|
);
|
|
802
823
|
|
|
803
|
-
this.transportEstablished = true;
|
|
804
824
|
this.setConnectionState("connected");
|
|
805
825
|
}
|
|
806
826
|
|
|
@@ -850,7 +870,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
850
870
|
return receiveParameters;
|
|
851
871
|
}
|
|
852
872
|
|
|
853
|
-
get remoteIsBundled() {
|
|
873
|
+
private get remoteIsBundled() {
|
|
854
874
|
const remoteSdp = this._remoteDescription;
|
|
855
875
|
if (!remoteSdp) {
|
|
856
876
|
return undefined;
|
|
@@ -861,6 +881,11 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
861
881
|
return bundle;
|
|
862
882
|
}
|
|
863
883
|
|
|
884
|
+
restartIce() {
|
|
885
|
+
this.needRestart = true;
|
|
886
|
+
this.needNegotiation();
|
|
887
|
+
}
|
|
888
|
+
|
|
864
889
|
async setRemoteDescription(sessionDescription: RTCSessionDescriptionInit) {
|
|
865
890
|
if (
|
|
866
891
|
!sessionDescription.sdp ||
|
|
@@ -953,9 +978,11 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
953
978
|
|
|
954
979
|
const iceTransport = dtlsTransport.iceTransport;
|
|
955
980
|
|
|
956
|
-
if (remoteMedia.iceParams
|
|
957
|
-
|
|
958
|
-
|
|
981
|
+
if (remoteMedia.iceParams) {
|
|
982
|
+
const renomination = !!remoteSdp.media.find(
|
|
983
|
+
(m) => m.direction === "inactive",
|
|
984
|
+
);
|
|
985
|
+
iceTransport.setRemoteParams(remoteMedia.iceParams, renomination);
|
|
959
986
|
|
|
960
987
|
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
961
988
|
// RFC 8445 S6.1.1
|
|
@@ -963,6 +990,9 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
963
990
|
iceTransport.connection.iceControlling = true;
|
|
964
991
|
}
|
|
965
992
|
}
|
|
993
|
+
if (remoteMedia.dtlsParams) {
|
|
994
|
+
dtlsTransport.setRemoteParams(remoteMedia.dtlsParams);
|
|
995
|
+
}
|
|
966
996
|
|
|
967
997
|
// # add ICE candidates
|
|
968
998
|
remoteMedia.iceCandidates.forEach(iceTransport.addRemoteCandidate);
|
|
@@ -1034,13 +1064,17 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1034
1064
|
const localCodecs = this.config.codecs[remoteMedia.kind] || [];
|
|
1035
1065
|
|
|
1036
1066
|
const existCodec = findCodecByMimeType(localCodecs, remoteCodec);
|
|
1037
|
-
if (!existCodec)
|
|
1067
|
+
if (!existCodec) {
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1038
1070
|
|
|
1039
1071
|
if (existCodec?.name.toLowerCase() === "rtx") {
|
|
1040
1072
|
const params = codecParametersFromString(existCodec.parameters ?? "");
|
|
1041
1073
|
const pt = params["apt"];
|
|
1042
1074
|
const origin = remoteMedia.rtp.codecs.find((c) => c.payloadType === pt);
|
|
1043
|
-
if (!origin)
|
|
1075
|
+
if (!origin) {
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1044
1078
|
return !!findCodecByMimeType(localCodecs, origin);
|
|
1045
1079
|
}
|
|
1046
1080
|
|
|
@@ -1314,22 +1348,18 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1314
1348
|
}
|
|
1315
1349
|
|
|
1316
1350
|
private async ensureCerts() {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
this.certificates.push(localCertificate);
|
|
1321
|
-
} else {
|
|
1322
|
-
dtlsTransport.localCertificate = this.certificates[0];
|
|
1323
|
-
}
|
|
1324
|
-
};
|
|
1351
|
+
if (!this.certificate) {
|
|
1352
|
+
this.certificate = await RTCDtlsTransport.SetupCertificate();
|
|
1353
|
+
}
|
|
1325
1354
|
|
|
1326
1355
|
for (const dtlsTransport of this.dtlsTransports) {
|
|
1327
|
-
|
|
1356
|
+
dtlsTransport.localCertificate = this.certificate;
|
|
1328
1357
|
}
|
|
1329
1358
|
}
|
|
1330
1359
|
|
|
1331
1360
|
async createAnswer() {
|
|
1332
1361
|
await this.ensureCerts();
|
|
1362
|
+
|
|
1333
1363
|
const description = this.buildAnswer();
|
|
1334
1364
|
return description.toJSON();
|
|
1335
1365
|
}
|
|
@@ -1392,11 +1422,11 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1392
1422
|
|
|
1393
1423
|
if (this.config.bundlePolicy !== "disable") {
|
|
1394
1424
|
const bundle = new GroupDescription("BUNDLE", []);
|
|
1395
|
-
description.media
|
|
1425
|
+
for (const media of description.media) {
|
|
1396
1426
|
if (media.direction !== "inactive") {
|
|
1397
1427
|
bundle.items.push(media.rtp.muxId!);
|
|
1398
1428
|
}
|
|
1399
|
-
}
|
|
1429
|
+
}
|
|
1400
1430
|
description.group.push(bundle);
|
|
1401
1431
|
}
|
|
1402
1432
|
|
|
@@ -1439,8 +1469,8 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1439
1469
|
|
|
1440
1470
|
function allMatch(...state: IceGathererState[]) {
|
|
1441
1471
|
return (
|
|
1442
|
-
all.filter((check) => state.includes(check.
|
|
1443
|
-
|
|
1472
|
+
all.filter((check) => state.includes(check.gatheringState)).length ===
|
|
1473
|
+
all.length
|
|
1444
1474
|
);
|
|
1445
1475
|
}
|
|
1446
1476
|
|
|
@@ -1450,9 +1480,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1450
1480
|
newState = "complete";
|
|
1451
1481
|
} else if (!all.length || allMatch("new", "complete")) {
|
|
1452
1482
|
newState = "new";
|
|
1453
|
-
} else if (
|
|
1454
|
-
all.map((check) => check.iceGather.gatheringState).includes("gathering")
|
|
1455
|
-
) {
|
|
1483
|
+
} else if (all.map((check) => check.gatheringState).includes("gathering")) {
|
|
1456
1484
|
newState = "gathering";
|
|
1457
1485
|
} else {
|
|
1458
1486
|
newState = "new";
|
|
@@ -1519,7 +1547,9 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1519
1547
|
log("connectionStateChange", state);
|
|
1520
1548
|
this.connectionState = state;
|
|
1521
1549
|
this.connectionStateChange.execute(state);
|
|
1522
|
-
if (this.onconnectionstatechange)
|
|
1550
|
+
if (this.onconnectionstatechange) {
|
|
1551
|
+
this.onconnectionstatechange();
|
|
1552
|
+
}
|
|
1523
1553
|
this.emit("connectionstatechange");
|
|
1524
1554
|
}
|
|
1525
1555
|
|
|
@@ -1599,11 +1629,10 @@ export function addTransportDescription(
|
|
|
1599
1629
|
dtlsTransport: RTCDtlsTransport,
|
|
1600
1630
|
) {
|
|
1601
1631
|
const iceTransport = dtlsTransport.iceTransport;
|
|
1602
|
-
const iceGatherer = iceTransport.iceGather;
|
|
1603
1632
|
|
|
1604
|
-
media.iceCandidates =
|
|
1605
|
-
media.iceCandidatesComplete =
|
|
1606
|
-
media.iceParams =
|
|
1633
|
+
media.iceCandidates = iceTransport.localCandidates;
|
|
1634
|
+
media.iceCandidatesComplete = iceTransport.gatheringState === "complete";
|
|
1635
|
+
media.iceParams = iceTransport.localParameters;
|
|
1607
1636
|
media.iceOptions = "trickle";
|
|
1608
1637
|
|
|
1609
1638
|
media.host = DISCARD_HOST;
|
|
@@ -1661,6 +1690,7 @@ export interface PeerConfig {
|
|
|
1661
1690
|
iceAdditionalHostAddresses: string[] | undefined;
|
|
1662
1691
|
iceUseIpv4: boolean;
|
|
1663
1692
|
iceUseIpv6: boolean;
|
|
1693
|
+
forceTurnTCP: boolean;
|
|
1664
1694
|
/** If provided, is called on each STUN request.
|
|
1665
1695
|
* Return `true` if a STUN response should be sent, false if it should be skipped. */
|
|
1666
1696
|
iceFilterStunResponse:
|
|
@@ -1670,6 +1700,7 @@ export interface PeerConfig {
|
|
|
1670
1700
|
dtls: Partial<{
|
|
1671
1701
|
keys: DtlsKeys;
|
|
1672
1702
|
}>;
|
|
1703
|
+
icePasswordPrefix: string | undefined;
|
|
1673
1704
|
bundlePolicy: BundlePolicy;
|
|
1674
1705
|
debug: Partial<{
|
|
1675
1706
|
/**% */
|
|
@@ -1737,10 +1768,12 @@ export const defaultPeerConfig: PeerConfig = {
|
|
|
1737
1768
|
iceUseIpv6: true,
|
|
1738
1769
|
iceFilterStunResponse: undefined,
|
|
1739
1770
|
iceFilterCandidatePair: undefined,
|
|
1771
|
+
icePasswordPrefix: undefined,
|
|
1740
1772
|
dtls: {},
|
|
1741
1773
|
bundlePolicy: "max-compat",
|
|
1742
1774
|
debug: {},
|
|
1743
1775
|
midSuffix: false,
|
|
1776
|
+
forceTurnTCP: false,
|
|
1744
1777
|
};
|
|
1745
1778
|
|
|
1746
1779
|
export interface RTCTrackEvent {
|
package/src/sdp.ts
CHANGED
|
@@ -563,15 +563,22 @@ function ipAddressToSdp(addr: string) {
|
|
|
563
563
|
|
|
564
564
|
export function candidateToSdp(c: IceCandidate) {
|
|
565
565
|
let sdp = `${c.foundation} ${c.component} ${c.protocol} ${c.priority} ${c.ip} ${c.port} typ ${c.type}`;
|
|
566
|
-
if (c.relatedAddress) {
|
|
566
|
+
if (c.relatedAddress != undefined) {
|
|
567
567
|
sdp += ` raddr ${c.relatedAddress}`;
|
|
568
568
|
}
|
|
569
|
-
if (c.relatedPort) {
|
|
569
|
+
if (c.relatedPort != undefined) {
|
|
570
570
|
sdp += ` rport ${c.relatedPort}`;
|
|
571
571
|
}
|
|
572
|
-
if (c.tcpType) {
|
|
572
|
+
if (c.tcpType != undefined) {
|
|
573
573
|
sdp += ` tcptype ${c.tcpType}`;
|
|
574
574
|
}
|
|
575
|
+
if (c.generation != undefined) {
|
|
576
|
+
sdp += ` generation ${c.generation}`;
|
|
577
|
+
}
|
|
578
|
+
if (c.ufrag != undefined) {
|
|
579
|
+
sdp += ` ufrag ${c.ufrag}`;
|
|
580
|
+
}
|
|
581
|
+
|
|
575
582
|
return sdp;
|
|
576
583
|
}
|
|
577
584
|
|
|
@@ -644,6 +651,12 @@ export function candidateFromSdp(sdp: string) {
|
|
|
644
651
|
case "tcptype":
|
|
645
652
|
candidate.tcpType = bits[i + 1];
|
|
646
653
|
break;
|
|
654
|
+
case "generation":
|
|
655
|
+
candidate.generation = Number.parseInt(bits[i + 1]);
|
|
656
|
+
break;
|
|
657
|
+
case "ufrag":
|
|
658
|
+
candidate.ufrag = bits[i + 1];
|
|
659
|
+
break;
|
|
647
660
|
}
|
|
648
661
|
});
|
|
649
662
|
|