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.
Files changed (175) hide show
  1. package/lib/common/src/event.d.ts +45 -0
  2. package/lib/common/src/event.js +229 -0
  3. package/lib/common/src/event.js.map +1 -0
  4. package/lib/common/src/index.d.ts +1 -0
  5. package/lib/common/src/index.js +1 -0
  6. package/lib/common/src/index.js.map +1 -1
  7. package/lib/dtls/src/flight/client/flight5.js +6 -1
  8. package/lib/dtls/src/flight/client/flight5.js.map +1 -1
  9. package/lib/dtls/src/flight/server/flight2.js +5 -3
  10. package/lib/dtls/src/flight/server/flight2.js.map +1 -1
  11. package/lib/dtls/src/imports/common.d.ts +1 -0
  12. package/lib/dtls/src/imports/common.js +18 -0
  13. package/lib/dtls/src/imports/common.js.map +1 -0
  14. package/lib/dtls/src/index.d.ts +1 -1
  15. package/lib/dtls/src/index.js.map +1 -1
  16. package/lib/dtls/src/record/receive.js +1 -1
  17. package/lib/dtls/src/record/receive.js.map +1 -1
  18. package/lib/dtls/src/socket.d.ts +1 -1
  19. package/lib/dtls/src/socket.js +5 -5
  20. package/lib/dtls/src/socket.js.map +1 -1
  21. package/lib/ice/src/candidate.d.ts +3 -2
  22. package/lib/ice/src/candidate.js +19 -5
  23. package/lib/ice/src/candidate.js.map +1 -1
  24. package/lib/ice/src/helper.d.ts +6 -6
  25. package/lib/ice/src/helper.js +20 -17
  26. package/lib/ice/src/helper.js.map +1 -1
  27. package/lib/ice/src/ice.d.ts +29 -71
  28. package/lib/ice/src/ice.js +417 -417
  29. package/lib/ice/src/ice.js.map +1 -1
  30. package/lib/ice/src/iceBase.d.ts +109 -0
  31. package/lib/ice/src/iceBase.js +166 -0
  32. package/lib/ice/src/iceBase.js.map +1 -0
  33. package/lib/ice/src/imports/common.d.ts +1 -0
  34. package/lib/ice/src/imports/common.js +18 -0
  35. package/lib/ice/src/imports/common.js.map +1 -0
  36. package/lib/ice/src/index.d.ts +5 -0
  37. package/lib/ice/src/index.js +5 -0
  38. package/lib/ice/src/index.js.map +1 -1
  39. package/lib/ice/src/stun/message.d.ts +7 -0
  40. package/lib/ice/src/stun/message.js +20 -6
  41. package/lib/ice/src/stun/message.js.map +1 -1
  42. package/lib/ice/src/stun/protocol.d.ts +6 -6
  43. package/lib/ice/src/stun/protocol.js +29 -23
  44. package/lib/ice/src/stun/protocol.js.map +1 -1
  45. package/lib/ice/src/stun/transaction.js +10 -4
  46. package/lib/ice/src/stun/transaction.js.map +1 -1
  47. package/lib/ice/src/transport.d.ts +21 -4
  48. package/lib/ice/src/transport.js +115 -7
  49. package/lib/ice/src/transport.js.map +1 -1
  50. package/lib/ice/src/turn/protocol.d.ts +54 -37
  51. package/lib/ice/src/turn/protocol.js +219 -94
  52. package/lib/ice/src/turn/protocol.js.map +1 -1
  53. package/lib/ice/src/types/model.d.ts +9 -5
  54. package/lib/ice/src/types/model.js.map +1 -1
  55. package/lib/ice/src/utils.d.ts +1 -0
  56. package/lib/ice/src/utils.js +15 -16
  57. package/lib/ice/src/utils.js.map +1 -1
  58. package/lib/rtp/src/extra/container/mp4/container.d.ts +1 -1
  59. package/lib/rtp/src/extra/container/mp4/container.js +2 -5
  60. package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
  61. package/lib/rtp/src/extra/container/webm/container.d.ts +3 -1
  62. package/lib/rtp/src/extra/container/webm/container.js +8 -2
  63. package/lib/rtp/src/extra/container/webm/container.js.map +1 -1
  64. package/lib/rtp/src/extra/container/webm/ebml/id.d.ts +6 -0
  65. package/lib/rtp/src/extra/container/webm/ebml/id.js +6 -0
  66. package/lib/rtp/src/extra/container/webm/ebml/id.js.map +1 -1
  67. package/lib/rtp/src/extra/processor/depacketizer.d.ts +1 -1
  68. package/lib/rtp/src/extra/processor/depacketizer.js +2 -2
  69. package/lib/rtp/src/extra/processor/depacketizer.js.map +1 -1
  70. package/lib/rtp/src/extra/processor/mp4.d.ts +1 -1
  71. package/lib/rtp/src/extra/processor/mp4.js +2 -5
  72. package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
  73. package/lib/rtp/src/extra/processor/nack.d.ts +1 -1
  74. package/lib/rtp/src/extra/processor/nack.js +3 -3
  75. package/lib/rtp/src/extra/processor/nack.js.map +1 -1
  76. package/lib/rtp/src/extra/processor/rtcpCallback.d.ts +1 -1
  77. package/lib/rtp/src/extra/processor/rtcpCallback.js +2 -5
  78. package/lib/rtp/src/extra/processor/rtcpCallback.js.map +1 -1
  79. package/lib/rtp/src/extra/processor/rtpCallback.d.ts +1 -1
  80. package/lib/rtp/src/extra/processor/rtpCallback.js +2 -5
  81. package/lib/rtp/src/extra/processor/rtpCallback.js.map +1 -1
  82. package/lib/rtp/src/extra/processor/webm.d.ts +12 -17
  83. package/lib/rtp/src/extra/processor/webm.js +2 -5
  84. package/lib/rtp/src/extra/processor/webm.js.map +1 -1
  85. package/lib/rtp/src/extra/processor/webmCallback.d.ts +1 -10
  86. package/lib/rtp/src/extra/processor/webmCallback.js.map +1 -1
  87. package/lib/rtp/src/imports/common.d.ts +1 -0
  88. package/lib/rtp/src/imports/common.js +18 -0
  89. package/lib/rtp/src/imports/common.js.map +1 -0
  90. package/lib/rtp/src/rtp/headerExtension.d.ts +45 -3
  91. package/lib/rtp/src/rtp/headerExtension.js +15 -0
  92. package/lib/rtp/src/rtp/headerExtension.js.map +1 -1
  93. package/lib/sctp/src/helper.js +2 -5
  94. package/lib/sctp/src/helper.js.map +1 -1
  95. package/lib/sctp/src/imports/common.d.ts +1 -0
  96. package/lib/sctp/src/imports/common.js +18 -0
  97. package/lib/sctp/src/imports/common.js.map +1 -0
  98. package/lib/sctp/src/index.d.ts +1 -1
  99. package/lib/sctp/src/index.js.map +1 -1
  100. package/lib/sctp/src/sctp.d.ts +1 -1
  101. package/lib/sctp/src/sctp.js +3 -3
  102. package/lib/sctp/src/sctp.js.map +1 -1
  103. package/lib/webrtc/src/const.d.ts +2 -2
  104. package/lib/webrtc/src/const.js.map +1 -1
  105. package/lib/webrtc/src/dataChannel.d.ts +1 -1
  106. package/lib/webrtc/src/dataChannel.js +5 -5
  107. package/lib/webrtc/src/dataChannel.js.map +1 -1
  108. package/lib/webrtc/src/imports/common.d.ts +1 -0
  109. package/lib/webrtc/src/imports/common.js +18 -0
  110. package/lib/webrtc/src/imports/common.js.map +1 -0
  111. package/lib/webrtc/src/media/extension/rtpExtension.d.ts +1 -0
  112. package/lib/webrtc/src/media/extension/rtpExtension.js +6 -0
  113. package/lib/webrtc/src/media/extension/rtpExtension.js.map +1 -1
  114. package/lib/webrtc/src/media/receiver/nack.d.ts +1 -1
  115. package/lib/webrtc/src/media/receiver/nack.js +2 -2
  116. package/lib/webrtc/src/media/receiver/nack.js.map +1 -1
  117. package/lib/webrtc/src/media/router.d.ts +0 -3
  118. package/lib/webrtc/src/media/router.js.map +1 -1
  119. package/lib/webrtc/src/media/rtpReceiver.d.ts +3 -4
  120. package/lib/webrtc/src/media/rtpReceiver.js +4 -7
  121. package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
  122. package/lib/webrtc/src/media/rtpSender.d.ts +1 -1
  123. package/lib/webrtc/src/media/rtpSender.js +5 -5
  124. package/lib/webrtc/src/media/rtpSender.js.map +1 -1
  125. package/lib/webrtc/src/media/rtpTransceiver.d.ts +1 -1
  126. package/lib/webrtc/src/media/rtpTransceiver.js +2 -5
  127. package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
  128. package/lib/webrtc/src/media/sender/senderBWE.d.ts +1 -1
  129. package/lib/webrtc/src/media/sender/senderBWE.js +4 -7
  130. package/lib/webrtc/src/media/sender/senderBWE.js.map +1 -1
  131. package/lib/webrtc/src/media/track.d.ts +3 -3
  132. package/lib/webrtc/src/media/track.js +4 -7
  133. package/lib/webrtc/src/media/track.js.map +1 -1
  134. package/lib/webrtc/src/nonstandard/recorder/index.d.ts +3 -1
  135. package/lib/webrtc/src/nonstandard/recorder/index.js +2 -5
  136. package/lib/webrtc/src/nonstandard/recorder/index.js.map +1 -1
  137. package/lib/webrtc/src/nonstandard/recorder/writer/index.d.ts +1 -1
  138. package/lib/webrtc/src/nonstandard/recorder/writer/index.js.map +1 -1
  139. package/lib/webrtc/src/nonstandard/recorder/writer/webm.d.ts +1 -1
  140. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +3 -2
  141. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
  142. package/lib/webrtc/src/peerConnection.d.ts +15 -8
  143. package/lib/webrtc/src/peerConnection.js +107 -93
  144. package/lib/webrtc/src/peerConnection.js.map +1 -1
  145. package/lib/webrtc/src/sdp.js +15 -3
  146. package/lib/webrtc/src/sdp.js.map +1 -1
  147. package/lib/webrtc/src/transport/dtls.d.ts +6 -6
  148. package/lib/webrtc/src/transport/dtls.js +19 -26
  149. package/lib/webrtc/src/transport/dtls.js.map +1 -1
  150. package/lib/webrtc/src/transport/ice.d.ts +22 -11
  151. package/lib/webrtc/src/transport/ice.js +106 -38
  152. package/lib/webrtc/src/transport/ice.js.map +1 -1
  153. package/lib/webrtc/src/transport/sctp.d.ts +1 -1
  154. package/lib/webrtc/src/transport/sctp.js +2 -2
  155. package/lib/webrtc/src/transport/sctp.js.map +1 -1
  156. package/package.json +1 -1
  157. package/src/const.ts +2 -2
  158. package/src/dataChannel.ts +1 -1
  159. package/src/imports/common.ts +1 -0
  160. package/src/media/extension/rtpExtension.ts +6 -0
  161. package/src/media/receiver/nack.ts +1 -1
  162. package/src/media/router.ts +2 -3
  163. package/src/media/rtpReceiver.ts +5 -4
  164. package/src/media/rtpSender.ts +1 -1
  165. package/src/media/rtpTransceiver.ts +1 -1
  166. package/src/media/sender/senderBWE.ts +1 -1
  167. package/src/media/track.ts +9 -4
  168. package/src/nonstandard/recorder/index.ts +4 -1
  169. package/src/nonstandard/recorder/writer/index.ts +1 -1
  170. package/src/nonstandard/recorder/writer/webm.ts +48 -44
  171. package/src/peerConnection.ts +122 -89
  172. package/src/sdp.ts +16 -3
  173. package/src/transport/dtls.ts +22 -14
  174. package/src/transport/ice.ts +83 -31
  175. package/src/transport/sctp.ts +1 -1
@@ -1,7 +1,12 @@
1
- import Event from "rx.mini";
2
1
  import { v4 } from "uuid";
3
-
4
- import { type RtcpPacket, type RtpHeader, RtpPacket } from "../../../rtp/src";
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 "rx.mini";
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 "rx.mini";
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((track, i) => {
32
- const trackNumber = i + 1;
33
- const payloadType = track.codec!.payloadType;
34
-
35
- if (track.kind === "video") {
36
- const codec = ((): SupportedCodec => {
37
- switch (track.codec?.name.toLowerCase() as SupportedVideoCodec) {
38
- case "vp8":
39
- return "VP8";
40
- case "vp9":
41
- return "VP9";
42
- case "h264":
43
- return "MPEG4/ISO/AVC";
44
- case "av1x":
45
- return "AV1";
46
- default:
47
- throw new WeriftError({
48
- message: "unsupported codec",
49
- payload: { track, path: sourcePath },
50
- });
51
- }
52
- })();
53
- return {
54
- kind: "video" as const,
55
- codec,
56
- clockRate: 90000,
57
- trackNumber,
58
- width: this.props.width ?? 640,
59
- height: this.props.height ?? 360,
60
- payloadType,
61
- track,
62
- };
63
- } else {
64
- return {
65
- kind: "audio" as const,
66
- codec: "OPUS" as const,
67
- clockRate: 48000,
68
- trackNumber,
69
- payloadType,
70
- track,
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,
@@ -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 readonly certificates: RTCCertificate[] = [];
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.certificates.push(
199
- new RTCCertificate(keys.keyPem, keys.certPem, keys.signatureHash),
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") return;
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.localUserName = existing.connection.localUserName;
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
- iceTransport.iceGather.onIceCandidate = (candidate) => {
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.certificates,
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.forEach((media, i) => {
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
- const setupRole = (dtlsTransport: RTCDtlsTransport) => {
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.forEach((t) => {
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
- // # gather candidates
671
- const connected = this.iceTransports.find(
672
- (transport) => transport.state === "connected",
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 && remoteMedia.dtlsParams) {
957
- iceTransport.setRemoteParams(remoteMedia.iceParams);
958
- dtlsTransport.setRemoteParams(remoteMedia.dtlsParams);
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) return false;
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) return false;
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
- const ensureCert = async (dtlsTransport: RTCDtlsTransport) => {
1318
- if (this.certificates.length === 0) {
1319
- const localCertificate = await dtlsTransport.setupCertificate();
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
- await ensureCert(dtlsTransport);
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.forEach((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.iceGather.gatheringState))
1443
- .length === all.length
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) 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 = iceGatherer.localCandidates;
1605
- media.iceCandidatesComplete = iceGatherer.gatheringState === "complete";
1606
- media.iceParams = iceGatherer.localParameters;
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