@agentuity/react 1.0.0 → 1.0.2
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/AGENTS.md +64 -61
- package/README.md +52 -0
- package/dist/eventstream.d.ts +20 -2
- package/dist/eventstream.d.ts.map +1 -1
- package/dist/eventstream.js +25 -1
- package/dist/eventstream.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/webrtc.d.ts +164 -0
- package/dist/webrtc.d.ts.map +1 -0
- package/dist/webrtc.js +301 -0
- package/dist/webrtc.js.map +1 -0
- package/package.json +5 -5
- package/src/eventstream.ts +29 -6
- package/src/index.ts +14 -0
- package/src/webrtc.tsx +483 -0
package/src/webrtc.tsx
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
WebRTCManager,
|
|
4
|
+
buildUrl,
|
|
5
|
+
type WebRTCManagerOptions,
|
|
6
|
+
type WebRTCClientCallbacks,
|
|
7
|
+
type TrackSource,
|
|
8
|
+
} from '@agentuity/frontend';
|
|
9
|
+
import type {
|
|
10
|
+
WebRTCConnectionState,
|
|
11
|
+
DataChannelConfig,
|
|
12
|
+
DataChannelState,
|
|
13
|
+
ConnectionQualitySummary,
|
|
14
|
+
RecordingHandle,
|
|
15
|
+
RecordingOptions,
|
|
16
|
+
} from '@agentuity/core';
|
|
17
|
+
|
|
18
|
+
export type {
|
|
19
|
+
WebRTCClientCallbacks,
|
|
20
|
+
DataChannelConfig,
|
|
21
|
+
DataChannelState,
|
|
22
|
+
ConnectionQualitySummary,
|
|
23
|
+
};
|
|
24
|
+
import { AgentuityContext } from './context';
|
|
25
|
+
|
|
26
|
+
export type { WebRTCConnectionState };
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Options for useWebRTCCall hook
|
|
30
|
+
*/
|
|
31
|
+
export interface UseWebRTCCallOptions {
|
|
32
|
+
/** Room ID to join */
|
|
33
|
+
roomId: string;
|
|
34
|
+
/** WebSocket signaling URL (e.g., '/call/signal' or full URL) */
|
|
35
|
+
signalUrl: string;
|
|
36
|
+
/** Whether this peer is "polite" in perfect negotiation */
|
|
37
|
+
polite?: boolean;
|
|
38
|
+
/** ICE servers configuration */
|
|
39
|
+
iceServers?: RTCIceServer[];
|
|
40
|
+
/**
|
|
41
|
+
* Media source configuration.
|
|
42
|
+
* - `false`: Data-only mode (no media)
|
|
43
|
+
* - `MediaStreamConstraints`: Use getUserMedia with these constraints
|
|
44
|
+
* - `TrackSource`: Use a custom track source
|
|
45
|
+
* Default: { video: true, audio: true }
|
|
46
|
+
*/
|
|
47
|
+
media?: MediaStreamConstraints | TrackSource | false;
|
|
48
|
+
/**
|
|
49
|
+
* Data channels to create when connection is established.
|
|
50
|
+
* Only the offerer (late joiner) creates channels; the answerer receives them.
|
|
51
|
+
*/
|
|
52
|
+
dataChannels?: DataChannelConfig[];
|
|
53
|
+
/**
|
|
54
|
+
* Whether to auto-reconnect on WebSocket/ICE failures (default: true)
|
|
55
|
+
*/
|
|
56
|
+
autoReconnect?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Maximum reconnection attempts before giving up (default: 5)
|
|
59
|
+
*/
|
|
60
|
+
maxReconnectAttempts?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Connection timeout in ms for connecting/negotiating (default: 30000)
|
|
63
|
+
*/
|
|
64
|
+
connectionTimeout?: number;
|
|
65
|
+
/**
|
|
66
|
+
* ICE gathering timeout in ms (default: 10000)
|
|
67
|
+
*/
|
|
68
|
+
iceGatheringTimeout?: number;
|
|
69
|
+
/** Whether to auto-connect on mount (default: true) */
|
|
70
|
+
autoConnect?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Optional callbacks for WebRTC events.
|
|
73
|
+
* These are called in addition to the hook's internal state management.
|
|
74
|
+
*/
|
|
75
|
+
callbacks?: Partial<WebRTCClientCallbacks>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Return type for useWebRTCCall hook
|
|
80
|
+
*/
|
|
81
|
+
export interface UseWebRTCCallResult {
|
|
82
|
+
/** Ref to attach to local video element */
|
|
83
|
+
localVideoRef: React.RefObject<HTMLVideoElement | null>;
|
|
84
|
+
/** Current connection state */
|
|
85
|
+
state: WebRTCConnectionState;
|
|
86
|
+
/** Current error if any */
|
|
87
|
+
error: Error | null;
|
|
88
|
+
/** Local peer ID assigned by server */
|
|
89
|
+
peerId: string | null;
|
|
90
|
+
/** Remote peer IDs */
|
|
91
|
+
remotePeerIds: string[];
|
|
92
|
+
/** Remote streams keyed by peer ID */
|
|
93
|
+
remoteStreams: Map<string, MediaStream>;
|
|
94
|
+
/** Whether audio is muted */
|
|
95
|
+
isAudioMuted: boolean;
|
|
96
|
+
/** Whether video is muted */
|
|
97
|
+
isVideoMuted: boolean;
|
|
98
|
+
/** Whether this is a data-only connection (no media) */
|
|
99
|
+
isDataOnly: boolean;
|
|
100
|
+
/** Whether screen sharing is active */
|
|
101
|
+
isScreenSharing: boolean;
|
|
102
|
+
/** Manually start the connection (if autoConnect is false) */
|
|
103
|
+
connect: () => void;
|
|
104
|
+
/** End the call */
|
|
105
|
+
hangup: () => void;
|
|
106
|
+
/** Mute or unmute audio */
|
|
107
|
+
muteAudio: (muted: boolean) => void;
|
|
108
|
+
/** Mute or unmute video */
|
|
109
|
+
muteVideo: (muted: boolean) => void;
|
|
110
|
+
|
|
111
|
+
// Screen sharing
|
|
112
|
+
/** Start screen sharing */
|
|
113
|
+
startScreenShare: (options?: DisplayMediaStreamOptions) => Promise<void>;
|
|
114
|
+
/** Stop screen sharing */
|
|
115
|
+
stopScreenShare: () => Promise<void>;
|
|
116
|
+
|
|
117
|
+
// Data channel methods
|
|
118
|
+
/** Create a new data channel to all peers */
|
|
119
|
+
createDataChannel: (config: DataChannelConfig) => Map<string, RTCDataChannel>;
|
|
120
|
+
/** Get all open data channel labels */
|
|
121
|
+
getDataChannelLabels: () => string[];
|
|
122
|
+
/** Get the state of a data channel for a specific peer */
|
|
123
|
+
getDataChannelState: (peerId: string, label: string) => DataChannelState | null;
|
|
124
|
+
/** Send a string message to all peers */
|
|
125
|
+
sendString: (label: string, data: string) => boolean;
|
|
126
|
+
/** Send a string message to a specific peer */
|
|
127
|
+
sendStringTo: (peerId: string, label: string, data: string) => boolean;
|
|
128
|
+
/** Send binary data to all peers */
|
|
129
|
+
sendBinary: (label: string, data: ArrayBuffer | Uint8Array) => boolean;
|
|
130
|
+
/** Send binary data to a specific peer */
|
|
131
|
+
sendBinaryTo: (peerId: string, label: string, data: ArrayBuffer | Uint8Array) => boolean;
|
|
132
|
+
/** Send JSON data to all peers */
|
|
133
|
+
sendJSON: (label: string, data: unknown) => boolean;
|
|
134
|
+
/** Send JSON data to a specific peer */
|
|
135
|
+
sendJSONTo: (peerId: string, label: string, data: unknown) => boolean;
|
|
136
|
+
/** Close a specific data channel on all peers */
|
|
137
|
+
closeDataChannel: (label: string) => boolean;
|
|
138
|
+
|
|
139
|
+
// Stats
|
|
140
|
+
/** Get quality summary for a peer */
|
|
141
|
+
getQualitySummary: (peerId: string) => Promise<ConnectionQualitySummary | null>;
|
|
142
|
+
/** Get quality summaries for all peers */
|
|
143
|
+
getAllQualitySummaries: () => Promise<Map<string, ConnectionQualitySummary>>;
|
|
144
|
+
|
|
145
|
+
// Recording
|
|
146
|
+
/** Start recording a stream */
|
|
147
|
+
startRecording: (streamId: string, options?: RecordingOptions) => RecordingHandle | null;
|
|
148
|
+
/** Check if a stream is being recorded */
|
|
149
|
+
isRecording: (streamId: string) => boolean;
|
|
150
|
+
/** Stop all recordings */
|
|
151
|
+
stopAllRecordings: () => Promise<Map<string, Blob>>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* React hook for WebRTC peer-to-peer audio/video/data calls.
|
|
156
|
+
*
|
|
157
|
+
* Supports multi-peer mesh networking, screen sharing, recording, and stats.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```tsx
|
|
161
|
+
* function VideoCall({ roomId }: { roomId: string }) {
|
|
162
|
+
* const {
|
|
163
|
+
* localVideoRef,
|
|
164
|
+
* state,
|
|
165
|
+
* remotePeerIds,
|
|
166
|
+
* remoteStreams,
|
|
167
|
+
* hangup,
|
|
168
|
+
* muteAudio,
|
|
169
|
+
* isAudioMuted,
|
|
170
|
+
* startScreenShare,
|
|
171
|
+
* } = useWebRTCCall({
|
|
172
|
+
* roomId,
|
|
173
|
+
* signalUrl: '/call/signal',
|
|
174
|
+
* callbacks: {
|
|
175
|
+
* onStateChange: (from, to, reason) => console.log(`${from} → ${to}`, reason),
|
|
176
|
+
* onRemoteStream: (peerId, stream) => console.log(`Got stream from ${peerId}`),
|
|
177
|
+
* },
|
|
178
|
+
* });
|
|
179
|
+
*
|
|
180
|
+
* return (
|
|
181
|
+
* <div>
|
|
182
|
+
* <video ref={localVideoRef} autoPlay muted playsInline />
|
|
183
|
+
* {remotePeerIds.map((peerId) => (
|
|
184
|
+
* <RemoteVideo key={peerId} stream={remoteStreams.get(peerId)} />
|
|
185
|
+
* ))}
|
|
186
|
+
* <p>State: {state}</p>
|
|
187
|
+
* <button onClick={() => muteAudio(!isAudioMuted)}>
|
|
188
|
+
* {isAudioMuted ? 'Unmute' : 'Mute'}
|
|
189
|
+
* </button>
|
|
190
|
+
* <button onClick={hangup}>Hang Up</button>
|
|
191
|
+
* </div>
|
|
192
|
+
* );
|
|
193
|
+
* }
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
export function useWebRTCCall(options: UseWebRTCCallOptions): UseWebRTCCallResult {
|
|
197
|
+
const context = useContext(AgentuityContext);
|
|
198
|
+
|
|
199
|
+
const managerRef = useRef<WebRTCManager | null>(null);
|
|
200
|
+
const localVideoRef = useRef<HTMLVideoElement | null>(null);
|
|
201
|
+
|
|
202
|
+
const [state, setState] = useState<WebRTCConnectionState>('idle');
|
|
203
|
+
const [error, setError] = useState<Error | null>(null);
|
|
204
|
+
const [peerId, setPeerId] = useState<string | null>(null);
|
|
205
|
+
const [remotePeerIds, setRemotePeerIds] = useState<string[]>([]);
|
|
206
|
+
const [remoteStreams, setRemoteStreams] = useState<Map<string, MediaStream>>(new Map());
|
|
207
|
+
const [isAudioMuted, setIsAudioMuted] = useState(false);
|
|
208
|
+
const [isVideoMuted, setIsVideoMuted] = useState(false);
|
|
209
|
+
const [isScreenSharing, setIsScreenSharing] = useState(false);
|
|
210
|
+
|
|
211
|
+
const userCallbacksRef = useRef(options.callbacks);
|
|
212
|
+
userCallbacksRef.current = options.callbacks;
|
|
213
|
+
|
|
214
|
+
const signalUrl = useMemo(() => {
|
|
215
|
+
if (options.signalUrl.startsWith('ws://') || options.signalUrl.startsWith('wss://')) {
|
|
216
|
+
return options.signalUrl;
|
|
217
|
+
}
|
|
218
|
+
const base = context?.baseUrl ?? window.location.origin;
|
|
219
|
+
const wsBase = base.replace(/^http(s?):/, 'ws$1:');
|
|
220
|
+
return buildUrl(wsBase, options.signalUrl);
|
|
221
|
+
}, [context?.baseUrl, options.signalUrl]);
|
|
222
|
+
|
|
223
|
+
const managerOptions = useMemo((): WebRTCManagerOptions => {
|
|
224
|
+
return {
|
|
225
|
+
signalUrl,
|
|
226
|
+
roomId: options.roomId,
|
|
227
|
+
polite: options.polite,
|
|
228
|
+
iceServers: options.iceServers,
|
|
229
|
+
media: options.media,
|
|
230
|
+
dataChannels: options.dataChannels,
|
|
231
|
+
autoReconnect: options.autoReconnect,
|
|
232
|
+
maxReconnectAttempts: options.maxReconnectAttempts,
|
|
233
|
+
connectionTimeout: options.connectionTimeout,
|
|
234
|
+
iceGatheringTimeout: options.iceGatheringTimeout,
|
|
235
|
+
callbacks: {
|
|
236
|
+
onStateChange: (from, to, reason) => {
|
|
237
|
+
setState(to);
|
|
238
|
+
if (managerRef.current) {
|
|
239
|
+
const managerState = managerRef.current.getState();
|
|
240
|
+
setPeerId(managerState.peerId);
|
|
241
|
+
setRemotePeerIds(managerState.remotePeerIds);
|
|
242
|
+
setIsScreenSharing(managerState.isScreenSharing);
|
|
243
|
+
}
|
|
244
|
+
userCallbacksRef.current?.onStateChange?.(from, to, reason);
|
|
245
|
+
},
|
|
246
|
+
onConnect: () => {
|
|
247
|
+
userCallbacksRef.current?.onConnect?.();
|
|
248
|
+
},
|
|
249
|
+
onDisconnect: (reason) => {
|
|
250
|
+
userCallbacksRef.current?.onDisconnect?.(reason);
|
|
251
|
+
},
|
|
252
|
+
onLocalStream: (stream) => {
|
|
253
|
+
if (localVideoRef.current) {
|
|
254
|
+
localVideoRef.current.srcObject = stream;
|
|
255
|
+
}
|
|
256
|
+
userCallbacksRef.current?.onLocalStream?.(stream);
|
|
257
|
+
},
|
|
258
|
+
onRemoteStream: (remotePeerId, stream) => {
|
|
259
|
+
setRemoteStreams((prev) => {
|
|
260
|
+
const next = new Map(prev);
|
|
261
|
+
next.set(remotePeerId, stream);
|
|
262
|
+
return next;
|
|
263
|
+
});
|
|
264
|
+
userCallbacksRef.current?.onRemoteStream?.(remotePeerId, stream);
|
|
265
|
+
},
|
|
266
|
+
onTrackAdded: (remotePeerId, track, stream) => {
|
|
267
|
+
userCallbacksRef.current?.onTrackAdded?.(remotePeerId, track, stream);
|
|
268
|
+
},
|
|
269
|
+
onTrackRemoved: (remotePeerId, track) => {
|
|
270
|
+
userCallbacksRef.current?.onTrackRemoved?.(remotePeerId, track);
|
|
271
|
+
},
|
|
272
|
+
onPeerJoined: (id) => {
|
|
273
|
+
setRemotePeerIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
|
|
274
|
+
userCallbacksRef.current?.onPeerJoined?.(id);
|
|
275
|
+
},
|
|
276
|
+
onPeerLeft: (id) => {
|
|
277
|
+
setRemotePeerIds((prev) => prev.filter((p) => p !== id));
|
|
278
|
+
setRemoteStreams((prev) => {
|
|
279
|
+
const next = new Map(prev);
|
|
280
|
+
next.delete(id);
|
|
281
|
+
return next;
|
|
282
|
+
});
|
|
283
|
+
userCallbacksRef.current?.onPeerLeft?.(id);
|
|
284
|
+
},
|
|
285
|
+
onNegotiationStart: (remotePeerId) => {
|
|
286
|
+
userCallbacksRef.current?.onNegotiationStart?.(remotePeerId);
|
|
287
|
+
},
|
|
288
|
+
onNegotiationComplete: (remotePeerId) => {
|
|
289
|
+
userCallbacksRef.current?.onNegotiationComplete?.(remotePeerId);
|
|
290
|
+
},
|
|
291
|
+
onIceCandidate: (remotePeerId, candidate) => {
|
|
292
|
+
userCallbacksRef.current?.onIceCandidate?.(remotePeerId, candidate);
|
|
293
|
+
},
|
|
294
|
+
onIceStateChange: (remotePeerId, iceState) => {
|
|
295
|
+
userCallbacksRef.current?.onIceStateChange?.(remotePeerId, iceState);
|
|
296
|
+
},
|
|
297
|
+
onError: (err, currentState) => {
|
|
298
|
+
setError(err);
|
|
299
|
+
userCallbacksRef.current?.onError?.(err, currentState);
|
|
300
|
+
},
|
|
301
|
+
onDataChannelOpen: (remotePeerId, label) => {
|
|
302
|
+
userCallbacksRef.current?.onDataChannelOpen?.(remotePeerId, label);
|
|
303
|
+
},
|
|
304
|
+
onDataChannelClose: (remotePeerId, label) => {
|
|
305
|
+
userCallbacksRef.current?.onDataChannelClose?.(remotePeerId, label);
|
|
306
|
+
},
|
|
307
|
+
onDataChannelMessage: (remotePeerId, label, data) => {
|
|
308
|
+
userCallbacksRef.current?.onDataChannelMessage?.(remotePeerId, label, data);
|
|
309
|
+
},
|
|
310
|
+
onDataChannelError: (remotePeerId, label, err) => {
|
|
311
|
+
userCallbacksRef.current?.onDataChannelError?.(remotePeerId, label, err);
|
|
312
|
+
},
|
|
313
|
+
onScreenShareStart: () => {
|
|
314
|
+
setIsScreenSharing(true);
|
|
315
|
+
userCallbacksRef.current?.onScreenShareStart?.();
|
|
316
|
+
},
|
|
317
|
+
onScreenShareStop: () => {
|
|
318
|
+
setIsScreenSharing(false);
|
|
319
|
+
userCallbacksRef.current?.onScreenShareStop?.();
|
|
320
|
+
},
|
|
321
|
+
onReconnecting: (attempt) => {
|
|
322
|
+
userCallbacksRef.current?.onReconnecting?.(attempt);
|
|
323
|
+
},
|
|
324
|
+
onReconnected: () => {
|
|
325
|
+
userCallbacksRef.current?.onReconnected?.();
|
|
326
|
+
},
|
|
327
|
+
onReconnectFailed: () => {
|
|
328
|
+
userCallbacksRef.current?.onReconnectFailed?.();
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}, [
|
|
333
|
+
signalUrl,
|
|
334
|
+
options.roomId,
|
|
335
|
+
options.polite,
|
|
336
|
+
options.iceServers,
|
|
337
|
+
options.media,
|
|
338
|
+
options.dataChannels,
|
|
339
|
+
options.autoReconnect,
|
|
340
|
+
options.maxReconnectAttempts,
|
|
341
|
+
options.connectionTimeout,
|
|
342
|
+
options.iceGatheringTimeout,
|
|
343
|
+
]);
|
|
344
|
+
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
const manager = new WebRTCManager(managerOptions);
|
|
347
|
+
managerRef.current = manager;
|
|
348
|
+
|
|
349
|
+
if (options.autoConnect !== false) {
|
|
350
|
+
manager.connect();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return () => {
|
|
354
|
+
manager.dispose();
|
|
355
|
+
managerRef.current = null;
|
|
356
|
+
};
|
|
357
|
+
}, [managerOptions, options.autoConnect]);
|
|
358
|
+
|
|
359
|
+
const connect = useCallback(() => {
|
|
360
|
+
managerRef.current?.connect();
|
|
361
|
+
}, []);
|
|
362
|
+
|
|
363
|
+
const hangup = useCallback(() => {
|
|
364
|
+
managerRef.current?.hangup();
|
|
365
|
+
setRemotePeerIds([]);
|
|
366
|
+
setRemoteStreams(new Map());
|
|
367
|
+
}, []);
|
|
368
|
+
|
|
369
|
+
const muteAudio = useCallback((muted: boolean) => {
|
|
370
|
+
managerRef.current?.muteAudio(muted);
|
|
371
|
+
setIsAudioMuted(muted);
|
|
372
|
+
}, []);
|
|
373
|
+
|
|
374
|
+
const muteVideo = useCallback((muted: boolean) => {
|
|
375
|
+
managerRef.current?.muteVideo(muted);
|
|
376
|
+
setIsVideoMuted(muted);
|
|
377
|
+
}, []);
|
|
378
|
+
|
|
379
|
+
const startScreenShare = useCallback(async (opts?: DisplayMediaStreamOptions) => {
|
|
380
|
+
await managerRef.current?.startScreenShare(opts);
|
|
381
|
+
}, []);
|
|
382
|
+
|
|
383
|
+
const stopScreenShare = useCallback(async () => {
|
|
384
|
+
await managerRef.current?.stopScreenShare();
|
|
385
|
+
}, []);
|
|
386
|
+
|
|
387
|
+
const createDataChannel = useCallback((config: DataChannelConfig) => {
|
|
388
|
+
return managerRef.current?.createDataChannel(config) ?? new Map();
|
|
389
|
+
}, []);
|
|
390
|
+
|
|
391
|
+
const getDataChannelLabels = useCallback(() => {
|
|
392
|
+
return managerRef.current?.getDataChannelLabels() ?? [];
|
|
393
|
+
}, []);
|
|
394
|
+
|
|
395
|
+
const getDataChannelState = useCallback((remotePeerId: string, label: string) => {
|
|
396
|
+
return managerRef.current?.getDataChannelState(remotePeerId, label) ?? null;
|
|
397
|
+
}, []);
|
|
398
|
+
|
|
399
|
+
const sendString = useCallback((label: string, data: string) => {
|
|
400
|
+
return managerRef.current?.sendString(label, data) ?? false;
|
|
401
|
+
}, []);
|
|
402
|
+
|
|
403
|
+
const sendStringTo = useCallback((remotePeerId: string, label: string, data: string) => {
|
|
404
|
+
return managerRef.current?.sendStringTo(remotePeerId, label, data) ?? false;
|
|
405
|
+
}, []);
|
|
406
|
+
|
|
407
|
+
const sendBinary = useCallback((label: string, data: ArrayBuffer | Uint8Array) => {
|
|
408
|
+
return managerRef.current?.sendBinary(label, data) ?? false;
|
|
409
|
+
}, []);
|
|
410
|
+
|
|
411
|
+
const sendBinaryTo = useCallback(
|
|
412
|
+
(remotePeerId: string, label: string, data: ArrayBuffer | Uint8Array) => {
|
|
413
|
+
return managerRef.current?.sendBinaryTo(remotePeerId, label, data) ?? false;
|
|
414
|
+
},
|
|
415
|
+
[]
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const sendJSON = useCallback((label: string, data: unknown) => {
|
|
419
|
+
return managerRef.current?.sendJSON(label, data) ?? false;
|
|
420
|
+
}, []);
|
|
421
|
+
|
|
422
|
+
const sendJSONTo = useCallback((remotePeerId: string, label: string, data: unknown) => {
|
|
423
|
+
return managerRef.current?.sendJSONTo(remotePeerId, label, data) ?? false;
|
|
424
|
+
}, []);
|
|
425
|
+
|
|
426
|
+
const closeDataChannel = useCallback((label: string) => {
|
|
427
|
+
return managerRef.current?.closeDataChannel(label) ?? false;
|
|
428
|
+
}, []);
|
|
429
|
+
|
|
430
|
+
const getQualitySummary = useCallback(async (remotePeerId: string) => {
|
|
431
|
+
return managerRef.current?.getQualitySummary(remotePeerId) ?? null;
|
|
432
|
+
}, []);
|
|
433
|
+
|
|
434
|
+
const getAllQualitySummaries = useCallback(async () => {
|
|
435
|
+
return managerRef.current?.getAllQualitySummaries() ?? new Map();
|
|
436
|
+
}, []);
|
|
437
|
+
|
|
438
|
+
const startRecording = useCallback((streamId: string, opts?: RecordingOptions) => {
|
|
439
|
+
return managerRef.current?.startRecording(streamId, opts) ?? null;
|
|
440
|
+
}, []);
|
|
441
|
+
|
|
442
|
+
const isRecordingFn = useCallback((streamId: string) => {
|
|
443
|
+
return managerRef.current?.isRecording(streamId) ?? false;
|
|
444
|
+
}, []);
|
|
445
|
+
|
|
446
|
+
const stopAllRecordings = useCallback(async () => {
|
|
447
|
+
return managerRef.current?.stopAllRecordings() ?? new Map();
|
|
448
|
+
}, []);
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
localVideoRef,
|
|
452
|
+
state,
|
|
453
|
+
error,
|
|
454
|
+
peerId,
|
|
455
|
+
remotePeerIds,
|
|
456
|
+
remoteStreams,
|
|
457
|
+
isAudioMuted,
|
|
458
|
+
isVideoMuted,
|
|
459
|
+
isDataOnly: options.media === false,
|
|
460
|
+
isScreenSharing,
|
|
461
|
+
connect,
|
|
462
|
+
hangup,
|
|
463
|
+
muteAudio,
|
|
464
|
+
muteVideo,
|
|
465
|
+
startScreenShare,
|
|
466
|
+
stopScreenShare,
|
|
467
|
+
createDataChannel,
|
|
468
|
+
getDataChannelLabels,
|
|
469
|
+
getDataChannelState,
|
|
470
|
+
sendString,
|
|
471
|
+
sendStringTo,
|
|
472
|
+
sendBinary,
|
|
473
|
+
sendBinaryTo,
|
|
474
|
+
sendJSON,
|
|
475
|
+
sendJSONTo,
|
|
476
|
+
closeDataChannel,
|
|
477
|
+
getQualitySummary,
|
|
478
|
+
getAllQualitySummaries,
|
|
479
|
+
startRecording,
|
|
480
|
+
isRecording: isRecordingFn,
|
|
481
|
+
stopAllRecordings,
|
|
482
|
+
};
|
|
483
|
+
}
|