werift 0.16.2 → 0.17.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 (47) hide show
  1. package/lib/dtls/src/server.js +3 -0
  2. package/lib/dtls/src/server.js.map +1 -1
  3. package/lib/dtls/src/socket.d.ts +4 -3
  4. package/lib/dtls/src/socket.js +9 -0
  5. package/lib/dtls/src/socket.js.map +1 -1
  6. package/lib/rtp/src/codec/av1.js +4 -4
  7. package/lib/rtp/src/codec/av1.js.map +1 -1
  8. package/lib/rtp/src/codec/h264.js +1 -1
  9. package/lib/rtp/src/codec/h264.js.map +1 -1
  10. package/lib/rtp/src/codec/index.d.ts +0 -2
  11. package/lib/rtp/src/codec/index.js +1 -21
  12. package/lib/rtp/src/codec/index.js.map +1 -1
  13. package/lib/rtp/src/codec/vp8.js +1 -1
  14. package/lib/rtp/src/codec/vp8.js.map +1 -1
  15. package/lib/rtp/src/codec/vp9.js +1 -1
  16. package/lib/rtp/src/codec/vp9.js.map +1 -1
  17. package/lib/rtp/src/processor/depacketizer.d.ts +2 -1
  18. package/lib/rtp/src/processor/depacketizer.js.map +1 -1
  19. package/lib/rtp/src/processor/interface.d.ts +3 -0
  20. package/lib/rtp/src/processor/interface.js +3 -0
  21. package/lib/rtp/src/processor/interface.js.map +1 -0
  22. package/lib/rtp/src/processor/jitterBuffer.d.ts +2 -1
  23. package/lib/rtp/src/processor/jitterBuffer.js.map +1 -1
  24. package/lib/rtp/src/rtcp/rtpfb/nack.js +11 -11
  25. package/lib/rtp/src/rtcp/rtpfb/nack.js.map +1 -1
  26. package/lib/rtp/src/rtp/red/packet.js +3 -3
  27. package/lib/rtp/src/rtp/red/packet.js.map +1 -1
  28. package/lib/webrtc/src/media/receiver/nack.d.ts +4 -4
  29. package/lib/webrtc/src/media/receiver/nack.js +21 -20
  30. package/lib/webrtc/src/media/receiver/nack.js.map +1 -1
  31. package/lib/webrtc/src/media/rtpSender.d.ts +1 -1
  32. package/lib/webrtc/src/media/rtpSender.js +7 -1
  33. package/lib/webrtc/src/media/rtpSender.js.map +1 -1
  34. package/lib/webrtc/src/peerConnection.d.ts +1 -0
  35. package/lib/webrtc/src/peerConnection.js +74 -23
  36. package/lib/webrtc/src/peerConnection.js.map +1 -1
  37. package/lib/webrtc/src/sdp.js +21 -0
  38. package/lib/webrtc/src/sdp.js.map +1 -1
  39. package/lib/webrtc/src/transport/dtls.d.ts +1 -0
  40. package/lib/webrtc/src/transport/dtls.js +13 -6
  41. package/lib/webrtc/src/transport/dtls.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/media/receiver/nack.ts +26 -23
  44. package/src/media/rtpSender.ts +13 -4
  45. package/src/peerConnection.ts +92 -31
  46. package/src/sdp.ts +30 -0
  47. package/src/transport/dtls.ts +15 -6
@@ -87,6 +87,7 @@ export class RTCPeerConnection extends EventTarget {
87
87
  signalingState: RTCSignalingState = "stable";
88
88
  negotiationneeded = false;
89
89
  readonly transceivers: RTCRtpTransceiver[] = [];
90
+ candidatesSent = new Set<string>();
90
91
 
91
92
  readonly iceGatheringStateChange = new Event<[IceGathererState]>();
92
93
  readonly iceConnectionStateChange = new Event<[RTCIceConnectionState]>();
@@ -298,6 +299,10 @@ export class RTCPeerConnection extends EventTarget {
298
299
  } else {
299
300
  const transceiver = this.getTransceiverByMid(mid);
300
301
  if (!transceiver) {
302
+ if (m.direction === "inactive") {
303
+ description.media.push(m);
304
+ return;
305
+ }
301
306
  throw new Error("transceiver not found");
302
307
  }
303
308
  transceiver.mLineIndex = i;
@@ -315,17 +320,20 @@ export class RTCPeerConnection extends EventTarget {
315
320
  this.transceivers
316
321
  .filter((t) => !description.media.find((m) => m.rtp.muxId === t.mid))
317
322
  .forEach((transceiver) => {
318
- transceiver.mLineIndex = description.media.length;
319
323
  if (transceiver.mid == undefined) {
320
324
  transceiver.mid = allocateMid(this.seenMid, "av");
321
325
  }
322
- description.media.push(
323
- createMediaDescriptionForTransceiver(
324
- transceiver,
325
- this.cname,
326
- transceiver.direction
327
- )
326
+ const mediaDescription = createMediaDescriptionForTransceiver(
327
+ transceiver,
328
+ this.cname,
329
+ transceiver.direction
328
330
  );
331
+ if (transceiver.mLineIndex === undefined) {
332
+ transceiver.mLineIndex = description.media.length;
333
+ description.media.push(mediaDescription);
334
+ } else {
335
+ description.media[transceiver.mLineIndex] = mediaDescription;
336
+ }
329
337
  });
330
338
 
331
339
  if (
@@ -341,10 +349,12 @@ export class RTCPeerConnection extends EventTarget {
341
349
 
342
350
  if (this.config.bundlePolicy !== "disable") {
343
351
  const mids = description.media
344
- .map((m) => m.rtp.muxId)
352
+ .map((m) => (m.direction !== "inactive" ? m.rtp.muxId : undefined))
345
353
  .filter((v) => v) as string[];
346
- const bundle = new GroupDescription("BUNDLE", mids);
347
- description.group.push(bundle);
354
+ if (mids.length) {
355
+ const bundle = new GroupDescription("BUNDLE", mids);
356
+ description.group.push(bundle);
357
+ }
348
358
  }
349
359
 
350
360
  return description;
@@ -408,13 +418,17 @@ export class RTCPeerConnection extends EventTarget {
408
418
  return;
409
419
  }
410
420
 
411
- if (transceiver.direction === "sendrecv") {
412
- transceiver.direction = "recvonly";
413
- } else if (
414
- transceiver.direction === "sendonly" ||
415
- transceiver.direction === "recvonly"
416
- ) {
421
+ if (transceiver.stopping || transceiver.stopped) {
417
422
  transceiver.direction = "inactive";
423
+ } else {
424
+ if (transceiver.direction === "sendrecv") {
425
+ transceiver.direction = "recvonly";
426
+ } else if (
427
+ transceiver.direction === "sendonly" ||
428
+ transceiver.direction === "recvonly"
429
+ ) {
430
+ transceiver.direction = "inactive";
431
+ }
418
432
  }
419
433
  this.needNegotiation();
420
434
  }
@@ -491,6 +505,16 @@ export class RTCPeerConnection extends EventTarget {
491
505
 
492
506
  candidate.foundation = "candidate:" + candidate.foundation;
493
507
 
508
+ // prevent ice candidates that have already been sent from being being resent
509
+ // when the connection is renegotiated during a later setLocalDescription call.
510
+ if (candidate.sdpMid) {
511
+ const candidateKey = `${candidate.foundation}:${candidate.sdpMid}`;
512
+ if (this.candidatesSent.has(candidateKey)) {
513
+ return;
514
+ }
515
+ this.candidatesSent.add(candidateKey);
516
+ }
517
+
494
518
  this.onIceCandidate.execute(candidate.toJSON());
495
519
  if (this.onicecandidate) {
496
520
  this.onicecandidate({ candidate: candidate.toJSON() });
@@ -800,14 +824,19 @@ export class RTCPeerConnection extends EventTarget {
800
824
 
801
825
  // # apply description
802
826
 
803
- const transports = enumerate(remoteSdp.media).map(([i, remoteMedia]) => {
827
+ const matchTransceiverWithMedia = (
828
+ transceiver: RTCRtpTransceiver,
829
+ media: MediaDescription
830
+ ) =>
831
+ transceiver.kind === media.kind &&
832
+ [undefined, media.rtp.muxId].includes(transceiver.mid);
833
+
834
+ let transports = enumerate(remoteSdp.media).map(([i, remoteMedia]) => {
804
835
  let dtlsTransport: RTCDtlsTransport | undefined;
805
836
 
806
837
  if (["audio", "video"].includes(remoteMedia.kind)) {
807
- let transceiver = this.transceivers.find(
808
- (t) =>
809
- t.kind === remoteMedia.kind &&
810
- [undefined, remoteMedia.rtp.muxId].includes(t.mid)
838
+ let transceiver = this.transceivers.find((t) =>
839
+ matchTransceiverWithMedia(t, remoteMedia)
811
840
  );
812
841
  if (!transceiver) {
813
842
  // create remote transceiver
@@ -816,6 +845,12 @@ export class RTCPeerConnection extends EventTarget {
816
845
  });
817
846
  transceiver.mid = remoteMedia.rtp.muxId;
818
847
  this.onRemoteTransceiverAdded.execute(transceiver);
848
+ } else {
849
+ if (transceiver.direction === "inactive" && transceiver.stopping) {
850
+ transceiver.stopped = true;
851
+ transceiver.currentDirection = "inactive";
852
+ return;
853
+ }
819
854
  }
820
855
 
821
856
  if (this.remoteIsBundled) {
@@ -876,7 +911,25 @@ export class RTCPeerConnection extends EventTarget {
876
911
  remoteMedia.dtlsParams.role === "client" ? "server" : "client";
877
912
  }
878
913
  return iceTransport;
879
- });
914
+ }) as RTCIceTransport[];
915
+
916
+ // filter out inactive transports
917
+ transports = transports.filter((iceTransport) => !!iceTransport);
918
+
919
+ const removedTransceivers = this.transceivers.filter(
920
+ (t) =>
921
+ remoteSdp.media.find((m) => matchTransceiverWithMedia(t, m)) ==
922
+ undefined
923
+ );
924
+
925
+ if (sessionDescription.type === "answer") {
926
+ for (const transceiver of removedTransceivers) {
927
+ // todo: handle answer side transceiver removal work.
928
+ // event should trigger to notify media source to stop.
929
+ transceiver.stop();
930
+ transceiver.stopped = true;
931
+ }
932
+ }
880
933
 
881
934
  if (remoteSdp.type === "offer") {
882
935
  this.setSignalingState("have-remote-offer");
@@ -1107,7 +1160,20 @@ export class RTCPeerConnection extends EventTarget {
1107
1160
  transceiver.options = options;
1108
1161
  this.router.registerRtpSender(transceiver.sender);
1109
1162
 
1110
- this.transceivers.push(transceiver);
1163
+ // reuse inactive
1164
+ const inactiveTransceiverIndex = this.transceivers.findIndex(
1165
+ (t) => t.currentDirection === "inactive"
1166
+ );
1167
+ const inactiveTransceiver = this.transceivers.find(
1168
+ (t) => t.currentDirection === "inactive"
1169
+ )!;
1170
+ if (inactiveTransceiverIndex > -1) {
1171
+ this.transceivers[inactiveTransceiverIndex] = transceiver;
1172
+ transceiver.mLineIndex = inactiveTransceiver.mLineIndex;
1173
+ inactiveTransceiver.currentDirection = "stopped";
1174
+ } else {
1175
+ this.transceivers.push(transceiver);
1176
+ }
1111
1177
  this.onTransceiverAdded.execute(transceiver);
1112
1178
 
1113
1179
  this.updateIceConnectionState();
@@ -1471,17 +1537,12 @@ export function addTransportDescription(
1471
1537
  media.iceParams = iceGatherer.localParameters;
1472
1538
  media.iceOptions = "trickle";
1473
1539
 
1474
- if (media.iceCandidates.length > 0) {
1475
- const candidate = media.iceCandidates[media.iceCandidates.length - 1];
1476
- media.host = candidate.ip;
1477
- media.port = candidate.port;
1478
- } else {
1479
- media.host = DISCARD_HOST;
1480
- media.port = DISCARD_PORT;
1481
- }
1540
+ media.host = DISCARD_HOST;
1541
+ media.port = DISCARD_PORT;
1482
1542
 
1483
1543
  if (media.direction === "inactive") {
1484
1544
  media.port = 0;
1545
+ media.msid = undefined;
1485
1546
  }
1486
1547
 
1487
1548
  if (!media.dtlsParams) {
package/src/sdp.ts CHANGED
@@ -97,6 +97,8 @@ export class SessionDescription {
97
97
  }
98
98
  });
99
99
 
100
+ const bundle = session.group.find((g) => g.semantic === "BUNDLE");
101
+
100
102
  mediaGroups.forEach((mediaLines) => {
101
103
  const target = mediaLines[0];
102
104
  const m = target.match(/^m=([^ ]+) ([0-9]+) ([A-Z/]+) (.+)/);
@@ -121,11 +123,13 @@ export class SessionDescription {
121
123
  [...session.dtlsFingerprints],
122
124
  session.dtlsRole
123
125
  );
126
+
124
127
  currentMedia.iceParams = new RTCIceParameters({
125
128
  iceLite: session.iceLite,
126
129
  usernameFragment: session.iceUsernameFragment,
127
130
  password: session.icePassword,
128
131
  });
132
+
129
133
  currentMedia.iceOptions = session.iceOptions;
130
134
  session.media.push(currentMedia);
131
135
 
@@ -262,6 +266,32 @@ export class SessionDescription {
262
266
  }
263
267
  });
264
268
 
269
+ if (
270
+ !currentMedia.iceParams.usernameFragment ||
271
+ !currentMedia.iceParams.password
272
+ ) {
273
+ if (
274
+ currentMedia.rtp.muxId &&
275
+ bundle &&
276
+ bundle.items.includes(currentMedia.rtp.muxId)
277
+ ) {
278
+ for (let i = 0; i < bundle.items.length; i++) {
279
+ if (!bundle.items.includes(i.toString())) continue;
280
+ const check = session.media[i];
281
+ if (
282
+ check.iceParams &&
283
+ check.iceParams.usernameFragment &&
284
+ check.iceParams.password
285
+ ) {
286
+ currentMedia.iceParams = {
287
+ ...check.iceParams,
288
+ };
289
+ break;
290
+ }
291
+ }
292
+ }
293
+ }
294
+
265
295
  if (!currentMedia.dtlsParams.role) {
266
296
  currentMedia.dtlsParams = undefined;
267
297
  }
@@ -134,11 +134,11 @@ export class RTCDtlsTransport {
134
134
  }
135
135
  this.dataReceiver(buf);
136
136
  });
137
- this.dtls.onClose.once(() => {
137
+ this.dtls.onClose.subscribe(() => {
138
138
  this.setState("closed");
139
139
  });
140
140
  this.dtls.onConnect.once(r);
141
- this.dtls.onError.once((error) => {
141
+ this.dtls.onError.subscribe((error) => {
142
142
  this.setState("failed");
143
143
  log("dtls failed", error);
144
144
  });
@@ -155,16 +155,18 @@ export class RTCDtlsTransport {
155
155
  if (this.srtpProfiles.length > 0) {
156
156
  this.startSrtp();
157
157
  }
158
+ this.dtls!.onConnect.subscribe(() => {
159
+ this.updateSrtpSession();
160
+ this.setState("connected");
161
+ });
158
162
  this.setState("connected");
163
+
159
164
  log("dtls connected");
160
165
  }
161
166
 
162
- startSrtp() {
167
+ updateSrtpSession() {
163
168
  if (!this.dtls) throw new Error();
164
169
 
165
- if (this.srtpStarted) return;
166
- this.srtpStarted = true;
167
-
168
170
  const profile = this.dtls.srtp.srtpProfile;
169
171
  if (!profile) {
170
172
  throw new Error("need srtpProfile");
@@ -185,6 +187,13 @@ export class RTCDtlsTransport {
185
187
  };
186
188
  this.srtp = new SrtpSession(config);
187
189
  this.srtcp = new SrtcpSession(config);
190
+ }
191
+
192
+ startSrtp() {
193
+ if (this.srtpStarted) return;
194
+ this.srtpStarted = true;
195
+
196
+ this.updateSrtpSession();
188
197
 
189
198
  this.iceTransport.connection.onData.subscribe((data) => {
190
199
  if (