@defcomm/meetingsdk 0.2.1 → 0.2.3
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/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +139 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +139 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -280,6 +280,8 @@ interface SFUClientOptions {
|
|
|
280
280
|
recording_id: string;
|
|
281
281
|
stopped_at: string;
|
|
282
282
|
}) => void;
|
|
283
|
+
/** Authoritative room snapshot received after join/reconnect/resync. */
|
|
284
|
+
onRoomState?: (state: any) => void;
|
|
283
285
|
onError?: (error: any) => void;
|
|
284
286
|
autoSubscribe?: boolean;
|
|
285
287
|
}
|
|
@@ -333,6 +335,8 @@ declare class SFUClient {
|
|
|
333
335
|
private trackToSource;
|
|
334
336
|
private localTrackIds;
|
|
335
337
|
private subscriberStatsTimer;
|
|
338
|
+
private roomSyncTimer;
|
|
339
|
+
private roomSyncInFlight;
|
|
336
340
|
private onStateChange;
|
|
337
341
|
private onParticipantJoined;
|
|
338
342
|
private onParticipantLeft;
|
|
@@ -352,12 +356,16 @@ declare class SFUClient {
|
|
|
352
356
|
private onTrackStateChanged;
|
|
353
357
|
private onRecordingStarted;
|
|
354
358
|
private onRecordingStopped;
|
|
359
|
+
private onRoomState;
|
|
355
360
|
private onError;
|
|
356
361
|
private autoSubscribe;
|
|
357
362
|
constructor(options?: SFUClientOptions);
|
|
358
363
|
private heartbeatTimer;
|
|
359
364
|
private startHeartbeat;
|
|
360
365
|
private stopHeartbeat;
|
|
366
|
+
private startRoomStateSync;
|
|
367
|
+
private stopRoomStateSync;
|
|
368
|
+
private syncRoomState;
|
|
361
369
|
private createId;
|
|
362
370
|
private resumeStorageKey;
|
|
363
371
|
private loadResumeToken;
|
|
@@ -392,6 +400,7 @@ declare class SFUClient {
|
|
|
392
400
|
private failJoin;
|
|
393
401
|
private resolveJoin;
|
|
394
402
|
private handleServerMessage;
|
|
403
|
+
private applyRoomState;
|
|
395
404
|
private rememberTrack;
|
|
396
405
|
private handleJoined;
|
|
397
406
|
private initializePeerConnections;
|
|
@@ -469,6 +478,7 @@ type MeetingContextValue = {
|
|
|
469
478
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
470
479
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
471
480
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
481
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
472
482
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
473
483
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
474
484
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
|
@@ -527,6 +537,7 @@ declare const useMeeting: (handlers?: {
|
|
|
527
537
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
528
538
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
529
539
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
540
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
530
541
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
531
542
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
532
543
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
|
@@ -540,6 +551,7 @@ declare const useRemoteMedia: (participantId: string) => {
|
|
|
540
551
|
audioRef: react.RefObject<HTMLAudioElement | null>;
|
|
541
552
|
isCamActive: boolean;
|
|
542
553
|
isMicEnabled: boolean;
|
|
554
|
+
connectionState: "connected" | "reconnecting" | "disconnected" | undefined;
|
|
543
555
|
};
|
|
544
556
|
|
|
545
557
|
type LiveRoomState = {
|
package/dist/index.d.ts
CHANGED
|
@@ -280,6 +280,8 @@ interface SFUClientOptions {
|
|
|
280
280
|
recording_id: string;
|
|
281
281
|
stopped_at: string;
|
|
282
282
|
}) => void;
|
|
283
|
+
/** Authoritative room snapshot received after join/reconnect/resync. */
|
|
284
|
+
onRoomState?: (state: any) => void;
|
|
283
285
|
onError?: (error: any) => void;
|
|
284
286
|
autoSubscribe?: boolean;
|
|
285
287
|
}
|
|
@@ -333,6 +335,8 @@ declare class SFUClient {
|
|
|
333
335
|
private trackToSource;
|
|
334
336
|
private localTrackIds;
|
|
335
337
|
private subscriberStatsTimer;
|
|
338
|
+
private roomSyncTimer;
|
|
339
|
+
private roomSyncInFlight;
|
|
336
340
|
private onStateChange;
|
|
337
341
|
private onParticipantJoined;
|
|
338
342
|
private onParticipantLeft;
|
|
@@ -352,12 +356,16 @@ declare class SFUClient {
|
|
|
352
356
|
private onTrackStateChanged;
|
|
353
357
|
private onRecordingStarted;
|
|
354
358
|
private onRecordingStopped;
|
|
359
|
+
private onRoomState;
|
|
355
360
|
private onError;
|
|
356
361
|
private autoSubscribe;
|
|
357
362
|
constructor(options?: SFUClientOptions);
|
|
358
363
|
private heartbeatTimer;
|
|
359
364
|
private startHeartbeat;
|
|
360
365
|
private stopHeartbeat;
|
|
366
|
+
private startRoomStateSync;
|
|
367
|
+
private stopRoomStateSync;
|
|
368
|
+
private syncRoomState;
|
|
361
369
|
private createId;
|
|
362
370
|
private resumeStorageKey;
|
|
363
371
|
private loadResumeToken;
|
|
@@ -392,6 +400,7 @@ declare class SFUClient {
|
|
|
392
400
|
private failJoin;
|
|
393
401
|
private resolveJoin;
|
|
394
402
|
private handleServerMessage;
|
|
403
|
+
private applyRoomState;
|
|
395
404
|
private rememberTrack;
|
|
396
405
|
private handleJoined;
|
|
397
406
|
private initializePeerConnections;
|
|
@@ -469,6 +478,7 @@ type MeetingContextValue = {
|
|
|
469
478
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
470
479
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
471
480
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
481
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
472
482
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
473
483
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
474
484
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
|
@@ -527,6 +537,7 @@ declare const useMeeting: (handlers?: {
|
|
|
527
537
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
528
538
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
529
539
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
540
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
530
541
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
531
542
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
532
543
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
|
@@ -540,6 +551,7 @@ declare const useRemoteMedia: (participantId: string) => {
|
|
|
540
551
|
audioRef: react.RefObject<HTMLAudioElement | null>;
|
|
541
552
|
isCamActive: boolean;
|
|
542
553
|
isMicEnabled: boolean;
|
|
554
|
+
connectionState: "connected" | "reconnecting" | "disconnected" | undefined;
|
|
543
555
|
};
|
|
544
556
|
|
|
545
557
|
type LiveRoomState = {
|
package/dist/index.js
CHANGED
|
@@ -428,6 +428,8 @@ var SFUClient = class {
|
|
|
428
428
|
this.trackToSource = /* @__PURE__ */ new Map();
|
|
429
429
|
this.localTrackIds = /* @__PURE__ */ new Map();
|
|
430
430
|
this.subscriberStatsTimer = null;
|
|
431
|
+
this.roomSyncTimer = null;
|
|
432
|
+
this.roomSyncInFlight = false;
|
|
431
433
|
this.heartbeatTimer = null;
|
|
432
434
|
this.apiBase = options.apiBase || "https://api.example.com/api";
|
|
433
435
|
this.wsBase = options.wsBase || "wss://api.example.com/ws";
|
|
@@ -470,6 +472,8 @@ var SFUClient = class {
|
|
|
470
472
|
});
|
|
471
473
|
this.onRecordingStopped = options.onRecordingStopped || (() => {
|
|
472
474
|
});
|
|
475
|
+
this.onRoomState = options.onRoomState || (() => {
|
|
476
|
+
});
|
|
473
477
|
this.onError = options.onError || console.error;
|
|
474
478
|
this.autoSubscribe = options.autoSubscribe !== false;
|
|
475
479
|
}
|
|
@@ -491,6 +495,29 @@ var SFUClient = class {
|
|
|
491
495
|
this.heartbeatTimer = null;
|
|
492
496
|
}
|
|
493
497
|
}
|
|
498
|
+
startRoomStateSync() {
|
|
499
|
+
this.stopRoomStateSync();
|
|
500
|
+
this.roomSyncTimer = setInterval(() => {
|
|
501
|
+
if (this.state !== "connected") return;
|
|
502
|
+
void this.syncRoomState();
|
|
503
|
+
}, 5e3);
|
|
504
|
+
}
|
|
505
|
+
stopRoomStateSync() {
|
|
506
|
+
if (this.roomSyncTimer) {
|
|
507
|
+
clearInterval(this.roomSyncTimer);
|
|
508
|
+
this.roomSyncTimer = null;
|
|
509
|
+
}
|
|
510
|
+
this.roomSyncInFlight = false;
|
|
511
|
+
}
|
|
512
|
+
async syncRoomState() {
|
|
513
|
+
if (this.roomSyncInFlight || !this.isSocketOpen()) return;
|
|
514
|
+
this.roomSyncInFlight = true;
|
|
515
|
+
try {
|
|
516
|
+
this.send({ type: "sync_room_state" });
|
|
517
|
+
} finally {
|
|
518
|
+
this.roomSyncInFlight = false;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
494
521
|
createId() {
|
|
495
522
|
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `id_${Math.random().toString(36).slice(2)}_${Date.now()}`;
|
|
496
523
|
}
|
|
@@ -913,6 +940,10 @@ var SFUClient = class {
|
|
|
913
940
|
case "subscription_updated": {
|
|
914
941
|
break;
|
|
915
942
|
}
|
|
943
|
+
case "room_state": {
|
|
944
|
+
await this.applyRoomState(payload);
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
916
947
|
case "recording_started": {
|
|
917
948
|
this.onRecordingStarted({
|
|
918
949
|
recording_id: payload.recording_id,
|
|
@@ -978,6 +1009,72 @@ var SFUClient = class {
|
|
|
978
1009
|
console.debug("[SFU] Ignoring unknown server message", message);
|
|
979
1010
|
}
|
|
980
1011
|
}
|
|
1012
|
+
async applyRoomState(payload) {
|
|
1013
|
+
const nextParticipants = /* @__PURE__ */ new Map();
|
|
1014
|
+
for (const participant of payload.participants || []) {
|
|
1015
|
+
const id = participant?.participant_id;
|
|
1016
|
+
if (!id) continue;
|
|
1017
|
+
nextParticipants.set(id, participant);
|
|
1018
|
+
}
|
|
1019
|
+
for (const [id] of this.participants) {
|
|
1020
|
+
if (id === this.currentParticipantId) continue;
|
|
1021
|
+
if (!nextParticipants.has(id)) {
|
|
1022
|
+
this.participants.delete(id);
|
|
1023
|
+
this.removeRemoteParticipant(id);
|
|
1024
|
+
this.onParticipantLeft(id);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
for (const [id, participant] of nextParticipants) {
|
|
1028
|
+
if (id === this.currentParticipantId) continue;
|
|
1029
|
+
const existed = this.participants.has(id);
|
|
1030
|
+
this.participants.set(id, participant);
|
|
1031
|
+
if (!existed) {
|
|
1032
|
+
this.onParticipantJoined(participant);
|
|
1033
|
+
}
|
|
1034
|
+
const sharing = Boolean(participant.screen_sharing);
|
|
1035
|
+
const screenTrackId = participant.screen_track_id ? String(participant.screen_track_id) : void 0;
|
|
1036
|
+
if (sharing) {
|
|
1037
|
+
const track = screenTrackId ? (payload.tracks || []).find(
|
|
1038
|
+
(candidate) => String(candidate.track_id) === screenTrackId
|
|
1039
|
+
) : void 0;
|
|
1040
|
+
this.onScreenShareStarted(id, track);
|
|
1041
|
+
} else if (this.remoteScreenStreams.has(id)) {
|
|
1042
|
+
this.remoteScreenStreams.get(id)?.getTracks().forEach((track) => track.stop());
|
|
1043
|
+
this.remoteScreenStreams.delete(id);
|
|
1044
|
+
this.onScreenShareStopped(id, screenTrackId);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
const nextTracks = /* @__PURE__ */ new Map();
|
|
1048
|
+
for (const track of payload.tracks || []) {
|
|
1049
|
+
if (track?.track_id) nextTracks.set(String(track.track_id), track);
|
|
1050
|
+
}
|
|
1051
|
+
for (const [trackId, existing] of this.trackMetadata) {
|
|
1052
|
+
if (!nextTracks.has(trackId)) {
|
|
1053
|
+
this.trackMetadata.delete(trackId);
|
|
1054
|
+
this.trackToParticipant.delete(trackId);
|
|
1055
|
+
this.trackToSource.delete(trackId);
|
|
1056
|
+
this.subscribedTracks.delete(trackId);
|
|
1057
|
+
for (const [source, localId] of this.localTrackIds) {
|
|
1058
|
+
if (localId === trackId) this.localTrackIds.delete(source);
|
|
1059
|
+
}
|
|
1060
|
+
if (existing.participant_id !== this.currentParticipantId) {
|
|
1061
|
+
this.onTrackUnpublished(existing);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
for (const track of nextTracks.values()) {
|
|
1066
|
+
this.rememberTrack(track);
|
|
1067
|
+
}
|
|
1068
|
+
if (this.autoSubscribe) {
|
|
1069
|
+
const missing = [...nextTracks.values()].filter(
|
|
1070
|
+
(track) => track.participant_id !== this.currentParticipantId && !this.subscribedTracks.has(track.track_id)
|
|
1071
|
+
).map((track) => track.track_id);
|
|
1072
|
+
if (missing.length) {
|
|
1073
|
+
await this.subscribeMultiple(missing);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
this.onRoomState(payload);
|
|
1077
|
+
}
|
|
981
1078
|
rememberTrack(track) {
|
|
982
1079
|
this.trackMetadata.set(track.track_id, track);
|
|
983
1080
|
this.trackToParticipant.set(track.track_id, track.participant_id);
|
|
@@ -1007,8 +1104,15 @@ var SFUClient = class {
|
|
|
1007
1104
|
for (const track of payload.tracks || []) {
|
|
1008
1105
|
if (track?.track_id) this.rememberTrack(track);
|
|
1009
1106
|
}
|
|
1107
|
+
if (payload.recording) {
|
|
1108
|
+
this.onRecordingStarted({
|
|
1109
|
+
recording_id: payload.recording.recording_id,
|
|
1110
|
+
started_at: payload.recording.started_at
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1010
1113
|
await this.initializePeerConnections();
|
|
1011
1114
|
this.setState("connected");
|
|
1115
|
+
this.startRoomStateSync();
|
|
1012
1116
|
this.onConnected(payload, resumed);
|
|
1013
1117
|
for (const participant of this.participants.values()) {
|
|
1014
1118
|
if (participant.participant_id !== this.currentParticipantId) {
|
|
@@ -1563,6 +1667,7 @@ var SFUClient = class {
|
|
|
1563
1667
|
}
|
|
1564
1668
|
async leave() {
|
|
1565
1669
|
this.stopHeartbeat();
|
|
1670
|
+
this.stopRoomStateSync();
|
|
1566
1671
|
this.intentionalClose = true;
|
|
1567
1672
|
this.setState("leaving");
|
|
1568
1673
|
if (this.reconnectTimer) {
|
|
@@ -1950,6 +2055,30 @@ var VideoSDKCore = class {
|
|
|
1950
2055
|
name: this.participantName
|
|
1951
2056
|
});
|
|
1952
2057
|
},
|
|
2058
|
+
onRoomState: (roomState) => {
|
|
2059
|
+
for (const participant of roomState?.participants || []) {
|
|
2060
|
+
const pid = participant?.participant_id;
|
|
2061
|
+
if (!pid || pid === this.myId) continue;
|
|
2062
|
+
const current = this.state.getParticipant(pid);
|
|
2063
|
+
if (!current) continue;
|
|
2064
|
+
if (participant.screen_sharing) {
|
|
2065
|
+
this.state.updateParticipantMedia(pid, {
|
|
2066
|
+
isScreenSharing: true,
|
|
2067
|
+
screenStream: current.media?.screenStream ?? null
|
|
2068
|
+
});
|
|
2069
|
+
} else if (current.media?.isScreenSharing) {
|
|
2070
|
+
const screen = this.remoteScreenStreams.get(pid);
|
|
2071
|
+
screen?.getTracks().forEach((track) => track.stop());
|
|
2072
|
+
this.remoteScreenStreams.delete(pid);
|
|
2073
|
+
this.state.updateParticipantMedia(pid, {
|
|
2074
|
+
isScreenSharing: false,
|
|
2075
|
+
screenStream: null,
|
|
2076
|
+
screenTrack: void 0
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
this.events.onRoomUpdate?.(roomState);
|
|
2081
|
+
},
|
|
1953
2082
|
onDisconnected: () => {
|
|
1954
2083
|
console.log("[SFU] Signaling WebSocket disconnected; reconnecting");
|
|
1955
2084
|
},
|
|
@@ -2509,6 +2638,7 @@ var MeetingProvider = ({
|
|
|
2509
2638
|
/* @__PURE__ */ new Set()
|
|
2510
2639
|
);
|
|
2511
2640
|
const meetingLeftListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2641
|
+
const roomUpdateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2512
2642
|
const trackStateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2513
2643
|
const recordingStartedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2514
2644
|
const recordingStoppedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
@@ -2533,6 +2663,9 @@ var MeetingProvider = ({
|
|
|
2533
2663
|
onMeetingLeft: () => {
|
|
2534
2664
|
meetingLeftListeners.current.forEach((fn) => fn());
|
|
2535
2665
|
},
|
|
2666
|
+
onRoomUpdate: (data) => {
|
|
2667
|
+
roomUpdateListeners.current.forEach((fn) => fn(data));
|
|
2668
|
+
},
|
|
2536
2669
|
onTrackStateChanged: (track) => {
|
|
2537
2670
|
trackStateListeners.current.forEach((fn) => fn(track));
|
|
2538
2671
|
},
|
|
@@ -2631,6 +2764,10 @@ var MeetingProvider = ({
|
|
|
2631
2764
|
meetingLeftListeners.current.delete(cb);
|
|
2632
2765
|
};
|
|
2633
2766
|
},
|
|
2767
|
+
onRoomUpdate: (cb) => {
|
|
2768
|
+
roomUpdateListeners.current.add(cb);
|
|
2769
|
+
return () => roomUpdateListeners.current.delete(cb);
|
|
2770
|
+
},
|
|
2634
2771
|
onTrackStateChanged: (cb) => {
|
|
2635
2772
|
trackStateListeners.current.add(cb);
|
|
2636
2773
|
return () => trackStateListeners.current.delete(cb);
|
|
@@ -2797,7 +2934,8 @@ var useRemoteMedia = (participantId) => {
|
|
|
2797
2934
|
videoRef,
|
|
2798
2935
|
audioRef,
|
|
2799
2936
|
isCamActive: !!participant?.media?.camEnabled,
|
|
2800
|
-
isMicEnabled: !!participant?.media?.micEnabled
|
|
2937
|
+
isMicEnabled: !!participant?.media?.micEnabled,
|
|
2938
|
+
connectionState: participant?.connectionState
|
|
2801
2939
|
};
|
|
2802
2940
|
};
|
|
2803
2941
|
|