werift 0.24.4 → 0.25.0-alpha.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 +116 -113
- package/lib/common/src/transport.d.ts +22 -2
- package/lib/common/src/transport.js +84 -26
- package/lib/common/src/transport.js.map +1 -1
- package/lib/ice/src/exceptions.d.ts +9 -0
- package/lib/ice/src/exceptions.js +23 -1
- package/lib/ice/src/exceptions.js.map +1 -1
- package/lib/ice/src/ice.d.ts +40 -0
- package/lib/ice/src/ice.js +194 -28
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +26 -0
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/stun/const.d.ts +7 -0
- package/lib/ice/src/stun/const.js +8 -0
- package/lib/ice/src/stun/const.js.map +1 -1
- package/lib/ice/src/stun/protocol.js +1 -4
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpProtocol.d.ts +7 -1
- package/lib/ice/src/stun/tcpProtocol.js +65 -14
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -1
- package/lib/ice/src/stun/transaction.d.ts +12 -1
- package/lib/ice/src/stun/transaction.js +37 -4
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/protocol.d.ts +40 -15
- package/lib/ice/src/turn/protocol.js +141 -32
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +8 -0
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/index.mjs +6175 -4073
- package/lib/nonstandard/index.mjs +286 -1003
- package/lib/polyfill/dom.mjs +23794 -0
- package/lib/polyfill/index.mjs +23794 -0
- package/lib/rtp/src/extra/container/mp4/container.js +12 -4
- package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4.js +8 -3
- package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4Callback.js +3 -1
- package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
- package/lib/sctp/src/chunk.d.ts +5 -0
- package/lib/sctp/src/chunk.js +16 -11
- package/lib/sctp/src/chunk.js.map +1 -1
- package/lib/sctp/src/index.d.ts +3 -1
- package/lib/sctp/src/index.js +10 -1
- package/lib/sctp/src/index.js.map +1 -1
- package/lib/sctp/src/sctp.d.ts +12 -3
- package/lib/sctp/src/sctp.js +51 -18
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/const.js +2 -0
- package/lib/webrtc/src/const.js.map +1 -1
- package/lib/webrtc/src/errors.d.ts +9 -1
- package/lib/webrtc/src/errors.js +19 -0
- package/lib/webrtc/src/errors.js.map +1 -1
- package/lib/webrtc/src/media/codec.d.ts +5 -0
- package/lib/webrtc/src/media/codec.js +21 -0
- package/lib/webrtc/src/media/codec.js.map +1 -1
- package/lib/webrtc/src/media/codecCompatibility.d.ts +23 -0
- package/lib/webrtc/src/media/codecCompatibility.js +138 -0
- package/lib/webrtc/src/media/codecCompatibility.js.map +1 -0
- package/lib/webrtc/src/media/index.d.ts +1 -0
- package/lib/webrtc/src/media/index.js +1 -0
- package/lib/webrtc/src/media/index.js.map +1 -1
- package/lib/webrtc/src/media/router.d.ts +2 -0
- package/lib/webrtc/src/media/router.js +23 -3
- package/lib/webrtc/src/media/router.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +12 -0
- package/lib/webrtc/src/media/rtpReceiver.js +35 -0
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +65 -4
- package/lib/webrtc/src/media/rtpSender.js +264 -35
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +42 -0
- package/lib/webrtc/src/media/rtpTransceiver.js +157 -4
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/track.d.ts +29 -1
- package/lib/webrtc/src/media/track.js +164 -5
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/index.d.ts +0 -1
- package/lib/webrtc/src/nonstandard/index.js +0 -1
- package/lib/webrtc/src/nonstandard/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +2 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +1 -2
- package/lib/webrtc/src/nonstandard/userMedia.js +30 -14
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +126 -5
- package/lib/webrtc/src/peerConnection.js +609 -136
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/polyfill/api.d.ts +14 -0
- package/lib/webrtc/src/polyfill/api.js +23 -0
- package/lib/webrtc/src/polyfill/api.js.map +1 -0
- package/lib/webrtc/src/polyfill/browserIdentity.d.ts +11 -0
- package/lib/webrtc/src/polyfill/browserIdentity.js +59 -0
- package/lib/webrtc/src/polyfill/browserIdentity.js.map +1 -0
- package/lib/webrtc/src/polyfill/dom.d.ts +11 -0
- package/lib/webrtc/src/polyfill/dom.js +18 -0
- package/lib/webrtc/src/polyfill/dom.js.map +1 -0
- package/lib/webrtc/src/polyfill/existingMediaDevices.d.ts +2 -0
- package/lib/webrtc/src/polyfill/existingMediaDevices.js +22 -0
- package/lib/webrtc/src/polyfill/existingMediaDevices.js.map +1 -0
- package/lib/webrtc/src/polyfill/globals.d.ts +28 -0
- package/lib/webrtc/src/polyfill/globals.js +3 -0
- package/lib/webrtc/src/polyfill/globals.js.map +1 -0
- package/lib/webrtc/src/polyfill/index.d.ts +2 -0
- package/lib/webrtc/src/polyfill/index.js +19 -0
- package/lib/webrtc/src/polyfill/index.js.map +1 -0
- package/lib/webrtc/src/polyfill/install.d.ts +13 -0
- package/lib/webrtc/src/polyfill/install.js +266 -0
- package/lib/webrtc/src/polyfill/install.js.map +1 -0
- package/lib/webrtc/src/polyfill/mediaDevices.d.ts +38 -0
- package/lib/webrtc/src/polyfill/mediaDevices.js +228 -0
- package/lib/webrtc/src/polyfill/mediaDevices.js.map +1 -0
- package/lib/webrtc/src/polyfill/mediaRegister.d.ts +51 -0
- package/lib/webrtc/src/polyfill/mediaRegister.js +14 -0
- package/lib/webrtc/src/polyfill/mediaRegister.js.map +1 -0
- package/lib/webrtc/src/polyfill/peerConnectionConfig.d.ts +3 -0
- package/lib/webrtc/src/polyfill/peerConnectionConfig.js +52 -0
- package/lib/webrtc/src/polyfill/peerConnectionConfig.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/callback.d.ts +16 -0
- package/lib/webrtc/src/polyfill/registers/callback.js +57 -0
- package/lib/webrtc/src/polyfill/registers/callback.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/empty.d.ts +2 -0
- package/lib/webrtc/src/polyfill/registers/empty.js +28 -0
- package/lib/webrtc/src/polyfill/registers/empty.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/mp4Webm.d.ts +36 -0
- package/lib/webrtc/src/polyfill/registers/mp4Webm.js +242 -0
- package/lib/webrtc/src/polyfill/registers/mp4Webm.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/rtpRtcp.d.ts +17 -0
- package/lib/webrtc/src/polyfill/registers/rtpRtcp.js +143 -0
- package/lib/webrtc/src/polyfill/registers/rtpRtcp.js.map +1 -0
- package/lib/webrtc/src/polyfill/rtcSessionDescription.d.ts +18 -0
- package/lib/webrtc/src/polyfill/rtcSessionDescription.js +36 -0
- package/lib/webrtc/src/polyfill/rtcSessionDescription.js.map +1 -0
- package/lib/webrtc/src/polyfill/rtpCodec.d.ts +17 -0
- package/lib/webrtc/src/polyfill/rtpCodec.js +93 -0
- package/lib/webrtc/src/polyfill/rtpCodec.js.map +1 -0
- package/lib/webrtc/src/polyfill/selectSettings.d.ts +31 -0
- package/lib/webrtc/src/polyfill/selectSettings.js +156 -0
- package/lib/webrtc/src/polyfill/selectSettings.js.map +1 -0
- package/lib/webrtc/src/polyfill/sourceIo.d.ts +21 -0
- package/lib/webrtc/src/polyfill/sourceIo.js +298 -0
- package/lib/webrtc/src/polyfill/sourceIo.js.map +1 -0
- package/lib/webrtc/src/polyfill/trackStop.d.ts +4 -0
- package/lib/webrtc/src/polyfill/trackStop.js +16 -0
- package/lib/webrtc/src/polyfill/trackStop.js.map +1 -0
- package/lib/webrtc/src/sctpManager.d.ts +2 -1
- package/lib/webrtc/src/sctpManager.js +5 -2
- package/lib/webrtc/src/sctpManager.js.map +1 -1
- package/lib/webrtc/src/sdp.d.ts +1 -1
- package/lib/webrtc/src/sdp.js +5 -6
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/sdpManager.d.ts +38 -1
- package/lib/webrtc/src/sdpManager.js +236 -30
- package/lib/webrtc/src/sdpManager.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +43 -12
- package/lib/webrtc/src/secureTransportManager.js +123 -47
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transceiverManager.d.ts +91 -6
- package/lib/webrtc/src/transceiverManager.js +477 -62
- package/lib/webrtc/src/transceiverManager.js.map +1 -1
- package/lib/webrtc/src/transport/ice.d.ts +8 -0
- package/lib/webrtc/src/transport/ice.js +17 -1
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +3 -2
- package/lib/webrtc/src/transport/sctp.js +8 -2
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/package.json +13 -3
|
@@ -5,10 +5,23 @@ const const_1 = require("./const");
|
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
const common_1 = require("./imports/common");
|
|
7
7
|
const media_1 = require("./media");
|
|
8
|
-
const
|
|
8
|
+
const codecCompatibility_1 = require("./media/codecCompatibility");
|
|
9
|
+
const track_1 = require("./media/track");
|
|
9
10
|
const sdp_1 = require("./sdp");
|
|
10
11
|
const utils_1 = require("./utils");
|
|
11
12
|
const log = (0, common_1.debug)("werift:packages/webrtc/src/media/rtpTransceiverManager.ts");
|
|
13
|
+
function simulcastFromSendEncodings(encodings) {
|
|
14
|
+
if (!encodings || encodings.length < 2) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const rids = encodings
|
|
18
|
+
.map((encoding) => encoding.rid)
|
|
19
|
+
.filter((rid) => typeof rid === "string" && rid.length > 0);
|
|
20
|
+
if (rids.length < 2) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return rids.map((rid) => ({ rid, direction: "send" }));
|
|
24
|
+
}
|
|
12
25
|
class TransceiverManager {
|
|
13
26
|
constructor(cname, config, router) {
|
|
14
27
|
Object.defineProperty(this, "cname", {
|
|
@@ -35,6 +48,18 @@ class TransceiverManager {
|
|
|
35
48
|
writable: true,
|
|
36
49
|
value: []
|
|
37
50
|
});
|
|
51
|
+
Object.defineProperty(this, "watched", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
writable: true,
|
|
55
|
+
value: new WeakSet()
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(this, "remoteOfferSnapshot", {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
writable: true,
|
|
61
|
+
value: void 0
|
|
62
|
+
});
|
|
38
63
|
Object.defineProperty(this, "onTransceiverAdded", {
|
|
39
64
|
enumerable: true,
|
|
40
65
|
configurable: true,
|
|
@@ -73,33 +98,121 @@ class TransceiverManager {
|
|
|
73
98
|
return this.transceivers.find((transceiver) => transceiver.mLineIndex === index);
|
|
74
99
|
}
|
|
75
100
|
pushTransceiver(t) {
|
|
101
|
+
this.watchTransceiver(t);
|
|
76
102
|
this.transceivers.push(t);
|
|
77
103
|
}
|
|
78
104
|
replaceTransceiver(t, index) {
|
|
105
|
+
this.watchTransceiver(t);
|
|
79
106
|
this.transceivers[index] = t;
|
|
80
107
|
}
|
|
81
|
-
|
|
108
|
+
/**m-line から外れた transceiver の購読を解除する */
|
|
109
|
+
unwatchTransceiver(t) {
|
|
110
|
+
this.watched.delete(t);
|
|
111
|
+
t.onRelease.allUnsubscribe();
|
|
112
|
+
t.onStopRequested.allUnsubscribe();
|
|
113
|
+
}
|
|
114
|
+
watchTransceiver(t) {
|
|
115
|
+
if (this.watched.has(t)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.watched.add(t);
|
|
119
|
+
t.onRelease.subscribe(() => {
|
|
120
|
+
this.router.unregisterTransceiver(t);
|
|
121
|
+
});
|
|
122
|
+
t.onStopRequested.subscribe(() => {
|
|
123
|
+
// 未関連付けの stop は m-line を作らないので交渉不要
|
|
124
|
+
if (!t.stopped) {
|
|
125
|
+
this.onNegotiationNeeded.execute();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 拒否 / 停止の交渉が確定した port 0 の位置のうち、同じ kind で
|
|
131
|
+
* まだ他の transceiver が予約していないものを返す。
|
|
132
|
+
* stopping (未交渉) の位置は先取りしない。
|
|
133
|
+
*/
|
|
134
|
+
findReusableTransceiver(kind) {
|
|
135
|
+
return this.transceivers.find((t) => t.stopped &&
|
|
136
|
+
t.kind === kind &&
|
|
137
|
+
t.mLineIndex != undefined &&
|
|
138
|
+
!this.transceivers.some((other) => other !== t && other.mLineIndex === t.mLineIndex));
|
|
139
|
+
}
|
|
140
|
+
/**旧 transceiver を m-line から外し、同じ位置に新しい transceiver を置く */
|
|
141
|
+
takeOverMLine(oldTransceiver, newTransceiver) {
|
|
142
|
+
const index = this.transceivers.indexOf(oldTransceiver);
|
|
143
|
+
newTransceiver.mLineIndex = oldTransceiver.mLineIndex;
|
|
144
|
+
oldTransceiver.mid = null;
|
|
145
|
+
oldTransceiver.mLineIndex = undefined;
|
|
146
|
+
this.unwatchTransceiver(oldTransceiver);
|
|
147
|
+
this.replaceTransceiver(newTransceiver, index);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 既存の未関連付け transceiver を remote m-line の位置に関連付ける。
|
|
151
|
+
* 同じ位置に停止済みの旧 transceiver があれば m-line から外し、配列上でも置き換える。
|
|
152
|
+
* (旧 transceiver が位置を持ったままだと MID の割り当て先が重複する)
|
|
153
|
+
*/
|
|
154
|
+
associateMLine(transceiver, mLineIndex) {
|
|
155
|
+
const previous = this.transceivers.find((t) => t !== transceiver && t.stopped && t.mLineIndex === mLineIndex);
|
|
156
|
+
if (!previous) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.transceivers.splice(this.transceivers.indexOf(transceiver), 1);
|
|
160
|
+
this.takeOverMLine(previous, transceiver);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* remote offer が定義した位置のうち、関連付けられなかった未交渉 transceiver の予約を解除する。
|
|
164
|
+
* (remote が予約位置を別 kind や別 transceiver で再利用した場合、次の offer で末尾に追加させる)
|
|
165
|
+
*/
|
|
166
|
+
releaseUnassociatedReservations(associated, mLineCount) {
|
|
167
|
+
for (const t of this.transceivers) {
|
|
168
|
+
if (!associated.has(t) &&
|
|
169
|
+
t.mid == null &&
|
|
170
|
+
t.mLineIndex != undefined &&
|
|
171
|
+
t.mLineIndex < mLineCount) {
|
|
172
|
+
t.mLineIndex = undefined;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
addTransceiver(trackOrKind, dtlsTransport, options = {}, { remoteMLineIndex, } = {}) {
|
|
82
177
|
const kind = typeof trackOrKind === "string" ? trackOrKind : trackOrKind.kind;
|
|
83
178
|
const direction = options.direction || "sendrecv";
|
|
84
|
-
|
|
179
|
+
if (typeof trackOrKind !== "string") {
|
|
180
|
+
(0, track_1.captureTrackSourceCodecs)(trackOrKind);
|
|
181
|
+
(0, codecCompatibility_1.assertCodecsSupported)({
|
|
182
|
+
kind,
|
|
183
|
+
configured: this.filterCodecsByDirection(kind, direction),
|
|
184
|
+
source: (0, track_1.getTrackSourceCodecs)(trackOrKind),
|
|
185
|
+
preferences: undefined,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const sender = new media_1.RTCRtpSender(trackOrKind, {
|
|
189
|
+
pendingRtp: this.config.pendingRtp,
|
|
190
|
+
});
|
|
85
191
|
const receiver = new media_1.RTCRtpReceiver(this.config, kind, sender.ssrc);
|
|
86
192
|
const newTransceiver = new media_1.RTCRtpTransceiver(kind, dtlsTransport, receiver, sender, direction);
|
|
87
|
-
newTransceiver.
|
|
193
|
+
newTransceiver.onCodecPreferencesChanged.subscribe(() => {
|
|
194
|
+
this.onNegotiationNeeded.execute();
|
|
195
|
+
});
|
|
196
|
+
newTransceiver.options = {
|
|
197
|
+
...options,
|
|
198
|
+
simulcast: options.simulcast ?? simulcastFromSendEncodings(options.sendEncodings),
|
|
199
|
+
};
|
|
88
200
|
newTransceiver.sender.setStreams(options.streams ?? []);
|
|
89
201
|
newTransceiver.sender.setSendEncodings((options.sendEncodings ?? []).map((encoding) => ({ ...encoding })));
|
|
90
202
|
this.router.registerRtpSender(newTransceiver.sender);
|
|
91
|
-
//
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
newTransceiver.mid = inactiveTransceiver.mid;
|
|
98
|
-
inactiveTransceiver.setCurrentDirection(undefined);
|
|
203
|
+
// 旧 transceiver は復活させず、MID は次の offer で新しく割り当てる
|
|
204
|
+
const reusable = remoteMLineIndex == undefined
|
|
205
|
+
? this.findReusableTransceiver(kind)
|
|
206
|
+
: this.transceivers.find((t) => t.stopped && t.mLineIndex === remoteMLineIndex);
|
|
207
|
+
if (reusable) {
|
|
208
|
+
this.takeOverMLine(reusable, newTransceiver);
|
|
99
209
|
}
|
|
100
210
|
else {
|
|
101
211
|
this.pushTransceiver(newTransceiver);
|
|
102
212
|
}
|
|
213
|
+
if (remoteMLineIndex != undefined) {
|
|
214
|
+
this.remoteOfferSnapshot?.remoteCreated.add(newTransceiver);
|
|
215
|
+
}
|
|
103
216
|
this.onTransceiverAdded.execute(newTransceiver);
|
|
104
217
|
return newTransceiver;
|
|
105
218
|
}
|
|
@@ -107,10 +220,16 @@ class TransceiverManager {
|
|
|
107
220
|
if (this.getSenders().find((sender) => sender.track?.uuid === track.uuid)) {
|
|
108
221
|
throw (0, errors_1.createWebRtcDomException)("InvalidAccessError", "Track already added");
|
|
109
222
|
}
|
|
110
|
-
const
|
|
223
|
+
const reusableForTrack = (t) => t.sender.track == undefined &&
|
|
111
224
|
t.kind === track.kind &&
|
|
112
|
-
|
|
225
|
+
!t.usedForSender &&
|
|
226
|
+
!t.stopping &&
|
|
227
|
+
!t.stopped &&
|
|
228
|
+
!t.rejected &&
|
|
229
|
+
!t.pendingRejection;
|
|
230
|
+
const emptyTrackSenderTransceiver = this.transceivers.find((t) => reusableForTrack(t) && const_1.SenderDirections.includes(t.direction));
|
|
113
231
|
if (emptyTrackSenderTransceiver) {
|
|
232
|
+
this.validateTrackForTransceiver(track, emptyTrackSenderTransceiver);
|
|
114
233
|
const sender = emptyTrackSenderTransceiver.sender;
|
|
115
234
|
sender.setStreams(streams);
|
|
116
235
|
sender.registerTrack(track);
|
|
@@ -118,13 +237,17 @@ class TransceiverManager {
|
|
|
118
237
|
...emptyTrackSenderTransceiver.options,
|
|
119
238
|
streams,
|
|
120
239
|
};
|
|
240
|
+
emptyTrackSenderTransceiver.codecs = [];
|
|
121
241
|
return emptyTrackSenderTransceiver;
|
|
122
242
|
}
|
|
123
|
-
const notSendTransceiver = this.transceivers.find((t) => t.
|
|
124
|
-
t.kind === track.kind &&
|
|
125
|
-
const_1.SenderDirections.includes(t.direction) === false &&
|
|
126
|
-
!t.usedForSender);
|
|
243
|
+
const notSendTransceiver = this.transceivers.find((t) => reusableForTrack(t) && !const_1.SenderDirections.includes(t.direction));
|
|
127
244
|
if (notSendTransceiver) {
|
|
245
|
+
const nextDirection = notSendTransceiver.direction === "recvonly"
|
|
246
|
+
? "sendrecv"
|
|
247
|
+
: notSendTransceiver.direction === "inactive"
|
|
248
|
+
? "sendonly"
|
|
249
|
+
: notSendTransceiver.direction;
|
|
250
|
+
this.validateTrackForTransceiver(track, notSendTransceiver, nextDirection);
|
|
128
251
|
const sender = notSendTransceiver.sender;
|
|
129
252
|
sender.setStreams(streams);
|
|
130
253
|
sender.registerTrack(track);
|
|
@@ -140,6 +263,7 @@ class TransceiverManager {
|
|
|
140
263
|
notSendTransceiver.setDirection("sendonly");
|
|
141
264
|
break;
|
|
142
265
|
}
|
|
266
|
+
notSendTransceiver.codecs = [];
|
|
143
267
|
return notSendTransceiver;
|
|
144
268
|
}
|
|
145
269
|
else {
|
|
@@ -150,6 +274,10 @@ class TransceiverManager {
|
|
|
150
274
|
return transceiver;
|
|
151
275
|
}
|
|
152
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* sender から track を外す。
|
|
279
|
+
* @returns 交渉が必要な変更をした場合 true (呼び出し側が negotiationneeded を要求する)
|
|
280
|
+
*/
|
|
153
281
|
removeTrack(sender) {
|
|
154
282
|
if (!this.getSenders().find(({ ssrc }) => sender.ssrc === ssrc)) {
|
|
155
283
|
throw (0, errors_1.createWebRtcDomException)("InvalidAccessError", "Sender does not exist");
|
|
@@ -158,36 +286,46 @@ class TransceiverManager {
|
|
|
158
286
|
if (!transceiver)
|
|
159
287
|
throw new Error("No matching transceiver found");
|
|
160
288
|
if (transceiver.stopping || transceiver.stopped) {
|
|
161
|
-
return;
|
|
289
|
+
return false;
|
|
162
290
|
}
|
|
163
|
-
sender.
|
|
164
|
-
|
|
165
|
-
this.onNegotiationNeeded.execute();
|
|
166
|
-
return;
|
|
291
|
+
if (sender.track == undefined) {
|
|
292
|
+
return false;
|
|
167
293
|
}
|
|
294
|
+
// sender 自体は止めず track だけ外し、同じ sender で送信を再開できるようにする
|
|
295
|
+
sender.detachTrack();
|
|
168
296
|
if (transceiver.direction === "sendrecv") {
|
|
169
297
|
transceiver.setDirection("recvonly");
|
|
170
298
|
}
|
|
171
|
-
else if (transceiver.direction === "sendonly"
|
|
172
|
-
transceiver.direction === "recvonly") {
|
|
299
|
+
else if (transceiver.direction === "sendonly") {
|
|
173
300
|
transceiver.setDirection("inactive");
|
|
174
301
|
}
|
|
302
|
+
return true;
|
|
175
303
|
}
|
|
176
304
|
assignTransceiverCodecs(transceiver) {
|
|
177
|
-
const
|
|
305
|
+
const configured = this.filterCodecsByDirection(transceiver.kind, transceiver.direction);
|
|
306
|
+
transceiver.codecs = (0, codecCompatibility_1.assertCodecsSupported)({
|
|
307
|
+
kind: transceiver.kind,
|
|
308
|
+
configured,
|
|
309
|
+
source: (0, track_1.getTrackSourceCodecs)(transceiver.sender.track),
|
|
310
|
+
preferences: transceiver.codecPreferences,
|
|
311
|
+
});
|
|
312
|
+
transceiver.codecPreferencesNeedResolution = false;
|
|
313
|
+
}
|
|
314
|
+
filterCodecsByDirection(kind, direction) {
|
|
315
|
+
return this.config.codecs[kind].filter((codecCandidate) => {
|
|
178
316
|
switch (codecCandidate.direction) {
|
|
179
317
|
case "recvonly": {
|
|
180
|
-
if (const_1.ReceiverDirection.includes(
|
|
318
|
+
if (const_1.ReceiverDirection.includes(direction))
|
|
181
319
|
return true;
|
|
182
320
|
return false;
|
|
183
321
|
}
|
|
184
322
|
case "sendonly": {
|
|
185
|
-
if (const_1.SenderDirections.includes(
|
|
323
|
+
if (const_1.SenderDirections.includes(direction))
|
|
186
324
|
return true;
|
|
187
325
|
return false;
|
|
188
326
|
}
|
|
189
327
|
case "sendrecv": {
|
|
190
|
-
if ([media_1.Sendrecv, media_1.Recvonly, media_1.Sendonly].includes(
|
|
328
|
+
if ([media_1.Sendrecv, media_1.Recvonly, media_1.Sendonly].includes(direction))
|
|
191
329
|
return true;
|
|
192
330
|
return false;
|
|
193
331
|
}
|
|
@@ -198,7 +336,15 @@ class TransceiverManager {
|
|
|
198
336
|
return false;
|
|
199
337
|
}
|
|
200
338
|
});
|
|
201
|
-
|
|
339
|
+
}
|
|
340
|
+
validateTrackForTransceiver(track, transceiver, direction = transceiver.direction) {
|
|
341
|
+
(0, track_1.captureTrackSourceCodecs)(track);
|
|
342
|
+
(0, codecCompatibility_1.assertCodecsSupported)({
|
|
343
|
+
kind: track.kind,
|
|
344
|
+
configured: this.filterCodecsByDirection(track.kind, direction),
|
|
345
|
+
source: (0, track_1.getTrackSourceCodecs)(track),
|
|
346
|
+
preferences: transceiver.codecPreferences,
|
|
347
|
+
});
|
|
202
348
|
}
|
|
203
349
|
getLocalRtpParams(transceiver) {
|
|
204
350
|
if (transceiver.mid == undefined)
|
|
@@ -235,33 +381,212 @@ class TransceiverManager {
|
|
|
235
381
|
};
|
|
236
382
|
return receiveParameters;
|
|
237
383
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
384
|
+
/**
|
|
385
|
+
* remote offer と local (source constraint + preferences) から
|
|
386
|
+
* answer 用の codec を再解決し、sender/receiver の codec 状態も
|
|
387
|
+
* 新しい negotiated codec に同期する。
|
|
388
|
+
* setCodecPreferences() による無効化後に createAnswer() から呼ばれる。
|
|
389
|
+
* direction / headerExtensions / onTrack は setRemoteDescription() 時の
|
|
390
|
+
* まま変えない (track イベントの重複発火を避ける)。
|
|
391
|
+
*/
|
|
392
|
+
resyncAnswerCodecs(transceiver, remoteMedia) {
|
|
393
|
+
this.refreshAnswerCodecs(transceiver, remoteMedia);
|
|
394
|
+
const localParams = this.getLocalRtpParams(transceiver);
|
|
395
|
+
transceiver.sender.prepareSend(localParams);
|
|
396
|
+
if (["recvonly", "sendrecv"].includes(transceiver.direction)) {
|
|
397
|
+
const remoteParams = this.getRemoteRtpParams(remoteMedia, transceiver);
|
|
398
|
+
for (const param of remoteMedia.simulcastParameters) {
|
|
399
|
+
this.router.registerRtpReceiverByRid(transceiver, param, remoteParams);
|
|
400
|
+
}
|
|
401
|
+
transceiver.receiver.resyncCodecs(remoteParams, remoteMedia.ssrc[0]?.ssrc);
|
|
402
|
+
this.router.registerRtpReceiverBySsrc(transceiver, remoteParams);
|
|
241
403
|
}
|
|
242
|
-
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* remote offer と local (source constraint + preferences) から
|
|
407
|
+
* answer 用の codec だけを再解決する (sender/receiver の同期なし)。
|
|
408
|
+
* setRemoteRTP() の交渉部分として使う。
|
|
409
|
+
*/
|
|
410
|
+
resolveRemoteCodecSet(localCodecs, remoteMedia, order, source) {
|
|
243
411
|
// # negotiate codecs
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
412
|
+
const codecName = (codec) => codec.name.toLowerCase();
|
|
413
|
+
const redPayloadTypes = (codec) => (codec.parameters ?? "")
|
|
414
|
+
.split("/")
|
|
415
|
+
.map(Number)
|
|
416
|
+
.filter((payloadType) => !Number.isNaN(payloadType));
|
|
417
|
+
const remoteByLocal = new Map();
|
|
418
|
+
const usedRemote = new Set();
|
|
419
|
+
for (const localCodec of localCodecs) {
|
|
420
|
+
if (["red", "rtx"].includes(codecName(localCodec)))
|
|
421
|
+
continue;
|
|
422
|
+
const remoteCodec = remoteMedia.rtp.codecs.find((codec) => !usedRemote.has(codec) &&
|
|
423
|
+
codec.mimeType.toLowerCase() === localCodec.mimeType.toLowerCase() &&
|
|
424
|
+
(source == undefined ||
|
|
425
|
+
source.some((sourceCodec) => (0, codecCompatibility_1.isCodecCompatible)(sourceCodec, codec))));
|
|
426
|
+
if (remoteCodec) {
|
|
427
|
+
remoteByLocal.set(localCodec, remoteCodec);
|
|
428
|
+
usedRemote.add(remoteCodec);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const negotiated = localCodecs.flatMap((localCodec) => {
|
|
432
|
+
if (codecName(localCodec) === "red") {
|
|
433
|
+
const remotePrimaryPayloadTypes = redPayloadTypes(localCodec).flatMap((payloadType) => {
|
|
434
|
+
const localPrimary = localCodecs.find((codec) => codec.payloadType === payloadType);
|
|
435
|
+
const remotePrimary = localPrimary
|
|
436
|
+
? remoteByLocal.get(localPrimary)
|
|
437
|
+
: undefined;
|
|
438
|
+
return remotePrimary ? [remotePrimary.payloadType] : [];
|
|
439
|
+
});
|
|
440
|
+
const remoteRed = remoteMedia.rtp.codecs.find((codec) => {
|
|
441
|
+
if (codecName(codec) !== "red" || usedRemote.has(codec))
|
|
442
|
+
return false;
|
|
443
|
+
const referenced = redPayloadTypes(codec);
|
|
444
|
+
return (referenced.length === remotePrimaryPayloadTypes.length &&
|
|
445
|
+
referenced.every((payloadType, index) => payloadType === remotePrimaryPayloadTypes[index]));
|
|
446
|
+
});
|
|
447
|
+
if (!remoteRed)
|
|
448
|
+
return [];
|
|
449
|
+
usedRemote.add(remoteRed);
|
|
450
|
+
return [remoteRed];
|
|
451
|
+
}
|
|
452
|
+
if (codecName(localCodec) !== "rtx") {
|
|
453
|
+
const remoteCodec = remoteByLocal.get(localCodec);
|
|
454
|
+
return remoteCodec ? [remoteCodec] : [];
|
|
249
455
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
456
|
+
const localApt = (0, sdp_1.codecParametersFromString)(localCodec.parameters ?? "").apt;
|
|
457
|
+
const localPrimary = localCodecs.find((codec) => codec.payloadType === localApt);
|
|
458
|
+
if (!localPrimary)
|
|
459
|
+
return [];
|
|
460
|
+
const remotePrimary = remoteByLocal.get(localPrimary);
|
|
461
|
+
if (!remotePrimary)
|
|
462
|
+
return [];
|
|
463
|
+
const remoteRtx = remoteMedia.rtp.codecs.find((codec) => {
|
|
464
|
+
if (codecName(codec) !== "rtx" || usedRemote.has(codec)) {
|
|
255
465
|
return false;
|
|
256
466
|
}
|
|
257
|
-
return
|
|
258
|
-
|
|
259
|
-
|
|
467
|
+
return ((0, sdp_1.codecParametersFromString)(codec.parameters ?? "").apt ===
|
|
468
|
+
remotePrimary.payloadType);
|
|
469
|
+
});
|
|
470
|
+
if (!remoteRtx)
|
|
471
|
+
return [];
|
|
472
|
+
usedRemote.add(remoteRtx);
|
|
473
|
+
return [remoteRtx];
|
|
474
|
+
});
|
|
475
|
+
if (order === "remote") {
|
|
476
|
+
const remoteOrder = new Map(remoteMedia.rtp.codecs.map((codec, index) => [codec, index]));
|
|
477
|
+
negotiated.sort((left, right) => remoteOrder.get(left) - remoteOrder.get(right));
|
|
478
|
+
}
|
|
479
|
+
if (negotiated.length === 0) {
|
|
480
|
+
throw (0, errors_1.createWebRtcDomException)("NotSupportedError", "No compatible codec remains after remote negotiation.");
|
|
481
|
+
}
|
|
482
|
+
return negotiated;
|
|
483
|
+
}
|
|
484
|
+
refreshAnswerCodecs(transceiver, remoteMedia) {
|
|
485
|
+
const configured = this.config.codecs[remoteMedia.kind] || [];
|
|
486
|
+
const source = (0, track_1.getTrackSourceCodecs)(transceiver.sender.track);
|
|
487
|
+
(0, codecCompatibility_1.assertCodecsSupported)({
|
|
488
|
+
kind: remoteMedia.kind,
|
|
489
|
+
configured,
|
|
490
|
+
source,
|
|
491
|
+
preferences: transceiver.codecPreferences,
|
|
260
492
|
});
|
|
493
|
+
const localCodecs = (0, codecCompatibility_1.resolveCodecs)(configured, source, transceiver.codecPreferences);
|
|
494
|
+
transceiver.codecs = this.resolveRemoteCodecSet(localCodecs, remoteMedia, "local", source);
|
|
495
|
+
transceiver.codecPreferencesNeedResolution = false;
|
|
261
496
|
log("negotiated codecs", transceiver.codecs);
|
|
262
|
-
|
|
263
|
-
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* remote SDP の全 audio/video m-line の codec を副作用なしで解決する。
|
|
500
|
+
* transceiver の対応付けは本適用と同じ resolver を使い、検証と適用の判定をずらさない。
|
|
501
|
+
* - port 0、停止済み / 停止中の m-line は codec 解決をしない (空)
|
|
502
|
+
* - local capability と MIME が 1 つも一致しない m-line は空とし、
|
|
503
|
+
* offer は setRemoteRTP() で拒否 (port 0 answer)、answer/pranswer は
|
|
504
|
+
* SDPManager が InvalidAccessError とする (Issue #705)
|
|
505
|
+
* - source constraint / preference / pending offer との不一致は NotSupportedError
|
|
506
|
+
*/
|
|
507
|
+
planRemoteRtpCodecs(remoteSdp, findTransceiver) {
|
|
508
|
+
const plan = new Map();
|
|
509
|
+
const isOffer = remoteSdp.type === "offer";
|
|
510
|
+
for (const [index, remoteMedia] of remoteSdp.media.entries()) {
|
|
511
|
+
if (!["audio", "video"].includes(remoteMedia.kind))
|
|
512
|
+
continue;
|
|
513
|
+
const transceiver = findTransceiver(remoteMedia, index);
|
|
514
|
+
if (remoteMedia.port === 0 ||
|
|
515
|
+
transceiver?.stopped ||
|
|
516
|
+
transceiver?.stopping) {
|
|
517
|
+
plan.set(index, []);
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const source = (0, track_1.getTrackSourceCodecs)(transceiver?.sender.track);
|
|
521
|
+
const configured = this.config.codecs[remoteMedia.kind] || [];
|
|
522
|
+
const localCodecs = isOffer
|
|
523
|
+
? configured
|
|
524
|
+
: transceiver?.pendingLocalOfferCodecs || [];
|
|
525
|
+
if (!(0, codecCompatibility_1.hasCommonPrimaryMimeType)(localCodecs, remoteMedia.rtp.codecs)) {
|
|
526
|
+
plan.set(index, []);
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (isOffer) {
|
|
530
|
+
(0, codecCompatibility_1.assertCodecsSupported)({
|
|
531
|
+
kind: remoteMedia.kind,
|
|
532
|
+
configured,
|
|
533
|
+
source,
|
|
534
|
+
preferences: transceiver?.codecPreferences,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
const negotiated = this.resolveRemoteCodecSet(isOffer
|
|
538
|
+
? (0, codecCompatibility_1.resolveCodecs)(configured, source, transceiver?.codecPreferences)
|
|
539
|
+
: localCodecs, remoteMedia, isOffer ? "local" : "remote", source);
|
|
540
|
+
if (source != undefined) {
|
|
541
|
+
(0, codecCompatibility_1.assertCodecsSupported)({
|
|
542
|
+
kind: remoteMedia.kind,
|
|
543
|
+
configured: negotiated,
|
|
544
|
+
source,
|
|
545
|
+
preferences: undefined,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
plan.set(index, negotiated);
|
|
549
|
+
}
|
|
550
|
+
return plan;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* remote m-line を transceiver に適用する。
|
|
554
|
+
* codecs は planRemoteRtpCodecs() で検証済みの解決結果を渡す。
|
|
555
|
+
* codec が空、または remote port 0 の m-line は拒否として扱い、
|
|
556
|
+
* sender/receiver 準備・router 登録・onTrack・TWCC を行わない。
|
|
557
|
+
* @returns 受け入れた (RTP を流す) 場合 true
|
|
558
|
+
*/
|
|
559
|
+
setRemoteRTP(transceiver, remoteMedia, type, mLineIndex, codecs) {
|
|
560
|
+
if (!transceiver.mid) {
|
|
561
|
+
transceiver.mid = remoteMedia.rtp.muxId ?? null;
|
|
562
|
+
}
|
|
563
|
+
transceiver.mLineIndex = mLineIndex;
|
|
564
|
+
if (type === "answer")
|
|
565
|
+
transceiver.pendingLocalOfferCodecs = undefined;
|
|
566
|
+
if (transceiver.stopped) {
|
|
567
|
+
// 確定済みの停止 / 拒否 m-line は復活させない
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
if (transceiver.stopping) {
|
|
571
|
+
// app の stop() は自分の offer で交渉する。port 0 の answer で停止を確定する
|
|
572
|
+
if (type === "answer" && remoteMedia.port === 0) {
|
|
573
|
+
transceiver.commitStopped({ rejected: false });
|
|
574
|
+
}
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
log("negotiated codecs", codecs);
|
|
578
|
+
if (remoteMedia.port === 0 || codecs.length === 0) {
|
|
579
|
+
if (type === "answer") {
|
|
580
|
+
transceiver.commitStopped({ rejected: true });
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
// answer が確定するまで既存の RTP pipeline / track は維持する
|
|
584
|
+
transceiver.pendingRejection = true;
|
|
585
|
+
}
|
|
586
|
+
return false;
|
|
264
587
|
}
|
|
588
|
+
transceiver.pendingRejection = false;
|
|
589
|
+
transceiver.codecs = codecs;
|
|
265
590
|
transceiver.headerExtensions = remoteMedia.rtp.headerExtensions.filter((extension) => (this.config.headerExtensions[remoteMedia.kind] ||
|
|
266
591
|
[]).find((v) => v.uri === extension.uri));
|
|
267
592
|
// # configure direction
|
|
@@ -281,12 +606,12 @@ class TransceiverManager {
|
|
|
281
606
|
for (const param of remoteMedia.simulcastParameters) {
|
|
282
607
|
this.router.registerRtpReceiverByRid(transceiver, param, remotePrams);
|
|
283
608
|
}
|
|
284
|
-
|
|
609
|
+
// codec map / RTX / TWCC を negotiated codec に合わせて再構築する
|
|
610
|
+
transceiver.receiver.resyncCodecs(remotePrams, remoteMedia.ssrc[0]?.ssrc);
|
|
285
611
|
// register ssrc receiver
|
|
286
612
|
this.router.registerRtpReceiverBySsrc(transceiver, remotePrams);
|
|
287
613
|
}
|
|
288
|
-
if (
|
|
289
|
-
["sendonly", "sendrecv"].includes(mediaDirection)) {
|
|
614
|
+
if (["sendonly", "sendrecv"].includes(mediaDirection)) {
|
|
290
615
|
const remoteStreamIds = [
|
|
291
616
|
...new Set(remoteMedia.msids.map((msid) => msid.split(" ")[0])),
|
|
292
617
|
];
|
|
@@ -294,18 +619,107 @@ class TransceiverManager {
|
|
|
294
619
|
transceiver.receiver.remoteStreamId = remoteStreamIds[0];
|
|
295
620
|
transceiver.receiver.remoteStreamIds = remoteStreamIds;
|
|
296
621
|
transceiver.receiver.remoteTrackId = remoteTrackId;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
transceiver
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
622
|
+
// re-offer / re-answer で同じ受信を重複通知しない
|
|
623
|
+
if (!transceiver.firedReceiving) {
|
|
624
|
+
transceiver.firedReceiving = true;
|
|
625
|
+
this.onTrack.execute({
|
|
626
|
+
track: transceiver.receiver.track,
|
|
627
|
+
transceiver,
|
|
628
|
+
streams: remoteStreamIds.map((id) => new media_1.MediaStream({
|
|
629
|
+
id,
|
|
630
|
+
tracks: [transceiver.receiver.track],
|
|
631
|
+
})),
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
transceiver.firedReceiving = false;
|
|
637
|
+
}
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* remote offer 適用前の transceiver 対応を保存する。
|
|
642
|
+
* 同じ offer/answer 交換中に複数回呼ばれても最初の状態を保持する。
|
|
643
|
+
*/
|
|
644
|
+
beginRemoteOffer() {
|
|
645
|
+
if (this.remoteOfferSnapshot) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
this.remoteOfferSnapshot = {
|
|
649
|
+
transceivers: [...this.transceivers],
|
|
650
|
+
remoteCreated: new Set(),
|
|
651
|
+
states: new Map(this.transceivers.map((t) => [
|
|
652
|
+
t,
|
|
653
|
+
{
|
|
654
|
+
mid: t.mid,
|
|
655
|
+
mLineIndex: t.mLineIndex,
|
|
656
|
+
pendingRejection: t.pendingRejection,
|
|
657
|
+
firedReceiving: t.firedReceiving,
|
|
658
|
+
},
|
|
659
|
+
])),
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* local answer の確定で、拒否予定の m-line を停止・解放する。
|
|
664
|
+
* remote SDP 起因の停止なので negotiationneeded は要求しない。
|
|
665
|
+
*/
|
|
666
|
+
commitRemoteOffer() {
|
|
667
|
+
this.remoteOfferSnapshot = undefined;
|
|
668
|
+
for (const transceiver of this.transceivers) {
|
|
669
|
+
if (transceiver.pendingRejection) {
|
|
670
|
+
transceiver.commitStopped({ rejected: true });
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
/**remote offer の rollback で transceiver 対応を元に戻す */
|
|
675
|
+
rollbackRemoteOffer() {
|
|
676
|
+
const snapshot = this.remoteOfferSnapshot;
|
|
677
|
+
this.remoteOfferSnapshot = undefined;
|
|
678
|
+
if (!snapshot) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
for (const transceiver of this.transceivers) {
|
|
682
|
+
if (snapshot.states.has(transceiver)) {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
// app が追加した transceiver と、remote offer が作ったが app が addTrack で使い始めた
|
|
686
|
+
// transceiver は残し、関連付けだけ外す (track の有無ではなく作成元で判定する)
|
|
687
|
+
if (!snapshot.remoteCreated.has(transceiver) ||
|
|
688
|
+
transceiver.sender.track) {
|
|
689
|
+
transceiver.mid = null;
|
|
690
|
+
transceiver.mLineIndex = undefined;
|
|
691
|
+
snapshot.transceivers.push(transceiver);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
// remote offer が作った transceiver は破棄する
|
|
695
|
+
transceiver.forceStop();
|
|
696
|
+
this.unwatchTransceiver(transceiver);
|
|
305
697
|
}
|
|
306
|
-
|
|
307
|
-
|
|
698
|
+
this.transceivers.splice(0, this.transceivers.length, ...snapshot.transceivers);
|
|
699
|
+
snapshot.transceivers.forEach((t) => this.watchTransceiver(t));
|
|
700
|
+
for (const [transceiver, state] of snapshot.states) {
|
|
701
|
+
transceiver.mid = state.mid;
|
|
702
|
+
transceiver.mLineIndex = state.mLineIndex;
|
|
703
|
+
transceiver.pendingRejection = state.pendingRejection;
|
|
704
|
+
transceiver.firedReceiving = state.firedReceiving;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* answer 確定後に、stopping のまま交渉対象になり得ない transceiver の停止を確定する。
|
|
709
|
+
* (MID が確定済み local description の非ゼロ m-line にない = offer から外れる)
|
|
710
|
+
* @param negotiatedMids 確定済み local description の非ゼロ port の MID
|
|
711
|
+
* @returns 次の自分の offer で port 0 を交渉すべき transceiver があるか
|
|
712
|
+
*/
|
|
713
|
+
settleStoppingTransceivers(negotiatedMids) {
|
|
714
|
+
for (const t of this.transceivers) {
|
|
715
|
+
if (!t.stopping || t.stopped) {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (t.mid == undefined || !negotiatedMids.has(t.mid)) {
|
|
719
|
+
t.commitStopped({ rejected: false });
|
|
720
|
+
}
|
|
308
721
|
}
|
|
722
|
+
return this.transceivers.some((t) => t.stopping && !t.stopped);
|
|
309
723
|
}
|
|
310
724
|
collectStats(timestamp) {
|
|
311
725
|
const stats = [];
|
|
@@ -344,6 +758,7 @@ class TransceiverManager {
|
|
|
344
758
|
close() {
|
|
345
759
|
for (const transceiver of this.transceivers) {
|
|
346
760
|
transceiver.forceStop();
|
|
761
|
+
this.unwatchTransceiver(transceiver);
|
|
347
762
|
}
|
|
348
763
|
this.onTransceiverAdded.allUnsubscribe();
|
|
349
764
|
this.onRemoteTransceiverAdded.allUnsubscribe();
|