@fishjam-cloud/react-client 0.5.1 → 0.5.5
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/Client.d.ts +71 -83
- package/dist/Client.d.ts.map +1 -1
- package/dist/Client.js +17 -145
- package/dist/DeviceManager.d.ts +4 -3
- package/dist/DeviceManager.d.ts.map +1 -1
- package/dist/DeviceManager.js +3 -0
- package/dist/create.d.ts +2 -3
- package/dist/create.d.ts.map +1 -1
- package/dist/create.js +111 -144
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/screenShareTrackManager.d.ts +4 -0
- package/dist/screenShareTrackManager.d.ts.map +1 -0
- package/dist/screenShareTrackManager.js +92 -0
- package/dist/state.types.d.ts +19 -24
- package/dist/state.types.d.ts.map +1 -1
- package/dist/trackManager.d.ts +7 -22
- package/dist/trackManager.d.ts.map +1 -1
- package/dist/trackManager.js +101 -84
- package/dist/types.d.ts +75 -51
- package/dist/types.d.ts.map +1 -1
- package/dist/useSetupMedia.d.ts +1 -1
- package/dist/useSetupMedia.d.ts.map +1 -1
- package/dist/useSetupMedia.js +19 -80
- package/dist/utils/track.d.ts +5 -0
- package/dist/utils/track.d.ts.map +1 -0
- package/dist/utils/track.js +37 -0
- package/package.json +6 -6
- package/readme.md +11 -29
- package/dist/ScreenShareManager.d.ts +0 -57
- package/dist/ScreenShareManager.d.ts.map +0 -1
- package/dist/ScreenShareManager.js +0 -135
package/dist/useSetupMedia.js
CHANGED
|
@@ -3,13 +3,10 @@ export const createUseSetupMediaHook = (useFishjamContext) => {
|
|
|
3
3
|
const isBroadcastedTrackChanged = (client, pending) => client.status === "joined" && !pending && !client.isReconnecting();
|
|
4
4
|
const isBroadcastedTrackStopped = (status, stream) => status === "joined" && stream;
|
|
5
5
|
return (config) => {
|
|
6
|
-
const { state } = useFishjamContext();
|
|
6
|
+
const { state, videoTrackManager, audioTrackManager } = useFishjamContext();
|
|
7
7
|
const configRef = useRef(config);
|
|
8
8
|
useEffect(() => {
|
|
9
9
|
configRef.current = config;
|
|
10
|
-
if (config.screenShare.streamConfig) {
|
|
11
|
-
state.client.setScreenManagerConfig(config.screenShare.streamConfig);
|
|
12
|
-
}
|
|
13
10
|
state.client.setDeviceManagerConfig({
|
|
14
11
|
storage: config.storage,
|
|
15
12
|
});
|
|
@@ -17,28 +14,23 @@ export const createUseSetupMediaHook = (useFishjamContext) => {
|
|
|
17
14
|
useEffect(() => {
|
|
18
15
|
if (!configRef.current.startOnMount)
|
|
19
16
|
return;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
videoTrackConstraints: configRef?.current.camera.trackConstraints,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
17
|
+
state.client.initializeDevices({
|
|
18
|
+
audioTrackConstraints: configRef?.current.microphone.trackConstraints,
|
|
19
|
+
videoTrackConstraints: configRef?.current.camera.trackConstraints,
|
|
20
|
+
});
|
|
27
21
|
// eslint-disable-next-line
|
|
28
22
|
}, []);
|
|
29
23
|
useEffect(() => {
|
|
30
24
|
let pending = false;
|
|
31
25
|
const broadcastOnCameraStart = async (client) => {
|
|
32
26
|
const config = configRef.current.camera;
|
|
33
|
-
const videoTrackManager = client.videoTrackManager;
|
|
34
27
|
const onDeviceChange = config.onDeviceChange ?? "replace";
|
|
35
|
-
const
|
|
36
|
-
const stream = camera.broadcast?.stream;
|
|
28
|
+
const stream = videoTrackManager.getCurrentTrack()?.stream;
|
|
37
29
|
if (isBroadcastedTrackChanged(client, pending)) {
|
|
38
30
|
if (!stream && config.broadcastOnDeviceStart) {
|
|
39
31
|
pending = true;
|
|
40
32
|
await videoTrackManager
|
|
41
|
-
.startStreaming(config.
|
|
33
|
+
.startStreaming(config.defaultSimulcastConfig, config.defaultMaxBandwidth)
|
|
42
34
|
.finally(() => {
|
|
43
35
|
pending = false;
|
|
44
36
|
});
|
|
@@ -80,12 +72,10 @@ export const createUseSetupMediaHook = (useFishjamContext) => {
|
|
|
80
72
|
state.client.removeListener("devicesReady", devicesReady);
|
|
81
73
|
state.client.removeListener("deviceReady", deviceReady);
|
|
82
74
|
};
|
|
83
|
-
}, [state.client]);
|
|
75
|
+
}, [state.client, videoTrackManager]);
|
|
84
76
|
useEffect(() => {
|
|
85
77
|
const removeOnCameraStopped = async (event, client) => {
|
|
86
|
-
const
|
|
87
|
-
const videoTrackManager = client.videoTrackManager;
|
|
88
|
-
const stream = camera.broadcast?.stream;
|
|
78
|
+
const stream = videoTrackManager.getCurrentTrack()?.stream;
|
|
89
79
|
const onDeviceStop = configRef.current.camera.onDeviceStop ?? "mute";
|
|
90
80
|
if (event.mediaDeviceType === "userMedia" &&
|
|
91
81
|
event.trackType === "video" &&
|
|
@@ -102,35 +92,31 @@ export const createUseSetupMediaHook = (useFishjamContext) => {
|
|
|
102
92
|
return () => {
|
|
103
93
|
state.client.removeListener("deviceStopped", removeOnCameraStopped);
|
|
104
94
|
};
|
|
105
|
-
}, [state.client]);
|
|
95
|
+
}, [state.client, videoTrackManager]);
|
|
106
96
|
useEffect(() => {
|
|
107
97
|
const broadcastCameraOnConnect = async (_, client) => {
|
|
108
98
|
const camera = client.devices.camera;
|
|
109
99
|
const stream = camera.stream;
|
|
110
100
|
const config = configRef.current.camera;
|
|
111
101
|
if (stream && config.broadcastOnConnect) {
|
|
112
|
-
await
|
|
102
|
+
await videoTrackManager.startStreaming(config.defaultSimulcastConfig, config.defaultMaxBandwidth);
|
|
113
103
|
}
|
|
114
104
|
};
|
|
115
105
|
state.client.on("joined", broadcastCameraOnConnect);
|
|
116
106
|
return () => {
|
|
117
107
|
state.client.removeListener("joined", broadcastCameraOnConnect);
|
|
118
108
|
};
|
|
119
|
-
}, [state.client]);
|
|
109
|
+
}, [state.client, videoTrackManager]);
|
|
120
110
|
useEffect(() => {
|
|
121
111
|
let pending = false;
|
|
122
112
|
const broadcastOnMicrophoneStart = async (client) => {
|
|
123
|
-
const
|
|
124
|
-
const audioTrackManager = client.audioTrackManager;
|
|
125
|
-
const stream = microphone.broadcast?.stream;
|
|
113
|
+
const stream = audioTrackManager.getCurrentTrack()?.stream;
|
|
126
114
|
const config = configRef.current.microphone;
|
|
127
115
|
const onDeviceChange = config.onDeviceChange ?? "replace";
|
|
128
116
|
if (isBroadcastedTrackChanged(client, pending)) {
|
|
129
117
|
if (!stream && config.broadcastOnDeviceStart) {
|
|
130
118
|
pending = true;
|
|
131
|
-
await audioTrackManager
|
|
132
|
-
.startStreaming(config.defaultTrackMetadata, undefined, config.defaultMaxBandwidth)
|
|
133
|
-
.finally(() => {
|
|
119
|
+
await audioTrackManager.startStreaming(undefined, config.defaultMaxBandwidth).finally(() => {
|
|
134
120
|
pending = false;
|
|
135
121
|
});
|
|
136
122
|
}
|
|
@@ -171,11 +157,10 @@ export const createUseSetupMediaHook = (useFishjamContext) => {
|
|
|
171
157
|
state.client.removeListener("deviceReady", deviceReady);
|
|
172
158
|
state.client.removeListener("devicesReady", devicesReady);
|
|
173
159
|
};
|
|
174
|
-
}, [state.client]);
|
|
160
|
+
}, [state.client, audioTrackManager]);
|
|
175
161
|
useEffect(() => {
|
|
176
162
|
const onMicrophoneStopped = async (event, client) => {
|
|
177
|
-
const
|
|
178
|
-
const stream = client.devices.microphone.broadcast?.stream;
|
|
163
|
+
const stream = audioTrackManager.getCurrentTrack()?.stream;
|
|
179
164
|
const onDeviceStop = configRef.current.microphone.onDeviceStop ?? "mute";
|
|
180
165
|
const isRightDeviceType = event.mediaDeviceType === "userMedia";
|
|
181
166
|
const isRightTrackType = event.trackType === "audio";
|
|
@@ -192,66 +177,20 @@ export const createUseSetupMediaHook = (useFishjamContext) => {
|
|
|
192
177
|
return () => {
|
|
193
178
|
state.client.removeListener("deviceStopped", onMicrophoneStopped);
|
|
194
179
|
};
|
|
195
|
-
}, [state.client]);
|
|
180
|
+
}, [state.client, audioTrackManager]);
|
|
196
181
|
useEffect(() => {
|
|
197
182
|
const broadcastMicrophoneOnConnect = async (_, client) => {
|
|
198
183
|
const config = configRef.current.microphone;
|
|
199
184
|
const microphone = client.devices.microphone;
|
|
200
185
|
if (microphone.stream && config.broadcastOnConnect) {
|
|
201
|
-
await
|
|
186
|
+
await audioTrackManager.startStreaming(undefined, config.defaultMaxBandwidth);
|
|
202
187
|
}
|
|
203
188
|
};
|
|
204
189
|
state.client.on("joined", broadcastMicrophoneOnConnect);
|
|
205
190
|
return () => {
|
|
206
191
|
state.client.removeListener("joined", broadcastMicrophoneOnConnect);
|
|
207
192
|
};
|
|
208
|
-
}, [state.client]);
|
|
209
|
-
useEffect(() => {
|
|
210
|
-
let pending = false;
|
|
211
|
-
const broadcastOnScreenShareStart = async (event, client) => {
|
|
212
|
-
const screenShare = client.devices.screenShare;
|
|
213
|
-
const stream = screenShare.broadcast?.stream;
|
|
214
|
-
const { broadcastOnDeviceStart, defaultTrackMetadata, defaultMaxBandwidth } = configRef.current.screenShare;
|
|
215
|
-
if (event.mediaDeviceType === "displayMedia" &&
|
|
216
|
-
isBroadcastedTrackChanged(client, pending) &&
|
|
217
|
-
!stream &&
|
|
218
|
-
broadcastOnDeviceStart) {
|
|
219
|
-
pending = true;
|
|
220
|
-
await screenShare.startStreaming(defaultTrackMetadata, defaultMaxBandwidth).finally(() => {
|
|
221
|
-
pending = false;
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
state.client.on("deviceReady", broadcastOnScreenShareStart);
|
|
226
|
-
return () => {
|
|
227
|
-
state.client.removeListener("deviceReady", broadcastOnScreenShareStart);
|
|
228
|
-
};
|
|
229
|
-
}, [state.client]);
|
|
230
|
-
useEffect(() => {
|
|
231
|
-
const onScreenShareStop = async (event, client) => {
|
|
232
|
-
const stream = client.devices.screenShare.broadcast?.stream;
|
|
233
|
-
const isRightDeviceType = event.mediaDeviceType === "displayMedia";
|
|
234
|
-
const isRightTrackType = event.trackType === "video";
|
|
235
|
-
if (isRightDeviceType && isRightTrackType && isBroadcastedTrackStopped(client.status, stream)) {
|
|
236
|
-
await client.devices.screenShare.stopStreaming();
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
state.client.on("deviceStopped", onScreenShareStop);
|
|
240
|
-
return () => {
|
|
241
|
-
state.client.removeListener("deviceStopped", onScreenShareStop);
|
|
242
|
-
};
|
|
243
|
-
}, [state.client]);
|
|
244
|
-
useEffect(() => {
|
|
245
|
-
const broadcastScreenShareOnConnect = async (_, client) => {
|
|
246
|
-
if (client.devices.screenShare.stream && configRef.current.screenShare.broadcastOnConnect) {
|
|
247
|
-
await client.devices.screenShare.startStreaming(configRef.current.screenShare.defaultTrackMetadata, configRef.current.screenShare.defaultMaxBandwidth);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
state.client.on("joined", broadcastScreenShareOnConnect);
|
|
251
|
-
return () => {
|
|
252
|
-
state.client.removeListener("joined", broadcastScreenShareOnConnect);
|
|
253
|
-
};
|
|
254
|
-
}, [state.client]);
|
|
193
|
+
}, [state.client, audioTrackManager]);
|
|
255
194
|
return useMemo(() => ({
|
|
256
195
|
init: () => state.client.initializeDevices({
|
|
257
196
|
audioTrackConstraints: configRef.current?.microphone?.trackConstraints,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FishjamClient } from "@fishjam-cloud/ts-client";
|
|
2
|
+
import type { Track } from "../state.types";
|
|
3
|
+
import type { PeerMetadata, TrackMetadata } from "../types";
|
|
4
|
+
export declare const getRemoteOrLocalTrack: (tsClient: FishjamClient<PeerMetadata, TrackMetadata>, remoteOrLocalTrackId: string | null) => Track | null;
|
|
5
|
+
//# sourceMappingURL=track.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/utils/track.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAsC5D,eAAO,MAAM,qBAAqB,aACtB,aAAa,CAAC,YAAY,EAAE,aAAa,CAAC,wBAC9B,MAAM,GAAG,IAAI,iBAKpC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// In most cases, the track is identified by its remote track ID.
|
|
2
|
+
// This ID comes from the ts-client `addTrack` method.
|
|
3
|
+
// However, we don't have that ID before the `addTrack` method returns it.
|
|
4
|
+
//
|
|
5
|
+
// The `addTrack` method emits the `localTrackAdded` event.
|
|
6
|
+
// This event will refresh the internal state of this object.
|
|
7
|
+
// However, in that event handler, we don't yet have the remote track ID.
|
|
8
|
+
// Therefore, for that brief moment, we will use the local track ID from the MediaStreamTrack object to identify the track.
|
|
9
|
+
const getRemoteOrLocalTrackContext = (tsClient, remoteOrLocalTrackId) => {
|
|
10
|
+
if (!remoteOrLocalTrackId)
|
|
11
|
+
return null;
|
|
12
|
+
const tracks = tsClient?.getLocalPeer()?.tracks;
|
|
13
|
+
if (!tracks)
|
|
14
|
+
return null;
|
|
15
|
+
const trackByRemoteId = tracks?.get(remoteOrLocalTrackId);
|
|
16
|
+
if (trackByRemoteId)
|
|
17
|
+
return trackByRemoteId;
|
|
18
|
+
const trackByLocalId = [...tracks.values()].find((track) => track.track?.id === remoteOrLocalTrackId);
|
|
19
|
+
return trackByLocalId ? trackByLocalId : null;
|
|
20
|
+
};
|
|
21
|
+
const getTrackFromContext = (context) => ({
|
|
22
|
+
rawMetadata: context.rawMetadata,
|
|
23
|
+
metadata: context.metadata,
|
|
24
|
+
trackId: context.trackId,
|
|
25
|
+
stream: context.stream,
|
|
26
|
+
simulcastConfig: context.simulcastConfig || null,
|
|
27
|
+
encoding: context.encoding || null,
|
|
28
|
+
vadStatus: context.vadStatus,
|
|
29
|
+
track: context.track,
|
|
30
|
+
metadataParsingError: context.metadataParsingError,
|
|
31
|
+
});
|
|
32
|
+
export const getRemoteOrLocalTrack = (tsClient, remoteOrLocalTrackId) => {
|
|
33
|
+
const context = getRemoteOrLocalTrackContext(tsClient, remoteOrLocalTrackId);
|
|
34
|
+
if (!context)
|
|
35
|
+
return null;
|
|
36
|
+
return getTrackFromContext(context);
|
|
37
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishjam-cloud/react-client",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "React client library for Fishjam Cloud",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Fishjam Cloud Team",
|
|
@@ -42,19 +42,19 @@
|
|
|
42
42
|
"typecheck": "tsc"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@playwright/test": "^1.
|
|
45
|
+
"@playwright/test": "^1.46.1",
|
|
46
46
|
"@types/events": "^3.0.3",
|
|
47
47
|
"@types/lodash.isequal": "^4.5.8",
|
|
48
|
-
"@types/node": "^22.
|
|
48
|
+
"@types/node": "^22.4.1",
|
|
49
49
|
"@types/react": "^18.2.55",
|
|
50
50
|
"react": "^18.2.0",
|
|
51
51
|
"typed-emitter": "^2.1.0",
|
|
52
|
-
"typedoc": "^0.26.
|
|
53
|
-
"typedoc-plugin-mdn-links": "^3.2.
|
|
52
|
+
"typedoc": "^0.26.6",
|
|
53
|
+
"typedoc-plugin-mdn-links": "^3.2.10",
|
|
54
54
|
"typescript": "^5.5.4"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@fishjam-cloud/ts-client": "0.5.
|
|
57
|
+
"@fishjam-cloud/ts-client": "0.5.5",
|
|
58
58
|
"events": "3.3.0",
|
|
59
59
|
"lodash.isequal": "4.5.0"
|
|
60
60
|
},
|
package/readme.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/@fishjam-cloud/react-client)
|
|
2
|
+
[](https://www.typescriptlang.org)
|
|
3
|
+
[](https://fishjam-cloud.github.io/web-client-sdk/modules/_fishjam_dev_react_client.html)
|
|
4
|
+
|
|
1
5
|
# Fishjam React Client
|
|
2
6
|
|
|
3
7
|
React client library for [Fishjam Cloud](https://cloud.fishjam.stream).
|
|
@@ -6,7 +10,7 @@ the [TS client](../ts-client/README.md).
|
|
|
6
10
|
|
|
7
11
|
## Documentation
|
|
8
12
|
|
|
9
|
-
Documentation is available [here](https://fishjam-cloud.github.io/web-client-sdk/modules/_fishjam_dev_react_client.html or you can generate it locally:
|
|
13
|
+
Documentation is available [here](https://fishjam-cloud.github.io/web-client-sdk/modules/_fishjam_dev_react_client.html) or you can generate it locally:
|
|
10
14
|
|
|
11
15
|
```bash
|
|
12
16
|
yarn run docs
|
|
@@ -14,26 +18,23 @@ yarn run docs
|
|
|
14
18
|
|
|
15
19
|
## Installation
|
|
16
20
|
|
|
17
|
-
You can install
|
|
21
|
+
You can install this package using `npm`:
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
24
|
npm install @fishjam-cloud/react-client
|
|
21
25
|
```
|
|
22
26
|
|
|
27
|
+
or `yarn`:
|
|
28
|
+
|
|
23
29
|
```bash
|
|
24
|
-
yarn
|
|
30
|
+
yarn @fishjam-cloud/react-client
|
|
25
31
|
```
|
|
26
32
|
|
|
27
|
-
It was tested with `nodejs` version mentioned in [`.tool-versions`](./.tool-versions) file.
|
|
28
|
-
|
|
29
33
|
## Usage
|
|
30
34
|
|
|
31
|
-
For pure TypeScript usage,
|
|
32
|
-
see [TS client](../ts-client/README.md).
|
|
33
|
-
|
|
34
35
|
Prerequisites:
|
|
35
36
|
|
|
36
|
-
- Account on Fishjam Cloud with App configured.
|
|
37
|
+
- Account on [Fishjam Cloud](https://cloud.fishjam.stream) with App configured.
|
|
37
38
|
- Created room and token of peer in that room.
|
|
38
39
|
You can use Room Manager to create room and peer token.
|
|
39
40
|
|
|
@@ -60,22 +61,9 @@ import { SCREEN_SHARING_MEDIA_CONSTRAINTS } from "@fishjam-cloud/react-client";
|
|
|
60
61
|
import { create } from "@fishjam-cloud/react-client";
|
|
61
62
|
import { useState } from "react";
|
|
62
63
|
|
|
63
|
-
// Example metadata types for peer and track
|
|
64
|
-
// You can define your own metadata types just make sure they are serializable
|
|
65
|
-
export type PeerMetadata = {
|
|
66
|
-
name: string;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export type TrackMetadata = {
|
|
70
|
-
type: "camera" | "screen";
|
|
71
|
-
};
|
|
72
|
-
|
|
73
64
|
// Create a Fishjam client instance
|
|
74
65
|
// remember to use FishjamContextProvider
|
|
75
|
-
export const { useApi, useTracks, useStatus, useConnect, useDisconnect, FishjamContextProvider } = create
|
|
76
|
-
PeerMetadata,
|
|
77
|
-
TrackMetadata
|
|
78
|
-
>();
|
|
66
|
+
export const { useApi, useTracks, useStatus, useConnect, useDisconnect, FishjamContextProvider } = create();
|
|
79
67
|
|
|
80
68
|
export const App = () => {
|
|
81
69
|
const [token, setToken] = useState("");
|
|
@@ -95,7 +83,6 @@ export const App = () => {
|
|
|
95
83
|
onClick={() => {
|
|
96
84
|
if (!token || token === "") throw Error("Token is empty");
|
|
97
85
|
connect({
|
|
98
|
-
peerMetadata: { name: "John Doe" }, // example metadata
|
|
99
86
|
token: token,
|
|
100
87
|
});
|
|
101
88
|
}}
|
|
@@ -133,11 +120,6 @@ export const App = () => {
|
|
|
133
120
|
};
|
|
134
121
|
```
|
|
135
122
|
|
|
136
|
-
### Releasing new versions
|
|
137
|
-
|
|
138
|
-
To release a new version of the package, go to `Actions` > `Release package` workflow and trigger it with the chosen release type.
|
|
139
|
-
The workflow will bump the package version in `package.json`, release the package to NPM, create a new git tag and a GitHub release.
|
|
140
|
-
|
|
141
123
|
## Examples
|
|
142
124
|
|
|
143
125
|
For examples, see [examples](../../examples/react-client/) folder.
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type TypedEmitter from "typed-emitter";
|
|
2
|
-
import type { DeviceError, DevicesStatus } from "./types";
|
|
3
|
-
import type { TrackKind } from "@fishjam-cloud/ts-client";
|
|
4
|
-
export type TrackType = TrackKind | "audiovideo";
|
|
5
|
-
export type MediaDeviceType = "displayMedia" | "userMedia";
|
|
6
|
-
export type DisplayMediaManagerEvents = {
|
|
7
|
-
deviceReady: (event: {
|
|
8
|
-
type: TrackType;
|
|
9
|
-
}, state: ScreenShareDeviceState) => void;
|
|
10
|
-
deviceStopped: (event: {
|
|
11
|
-
type: TrackType;
|
|
12
|
-
}, state: ScreenShareDeviceState) => void;
|
|
13
|
-
deviceEnabled: (event: {
|
|
14
|
-
type: TrackType;
|
|
15
|
-
}, state: ScreenShareDeviceState) => void;
|
|
16
|
-
deviceDisabled: (event: {
|
|
17
|
-
type: TrackType;
|
|
18
|
-
}, state: ScreenShareDeviceState) => void;
|
|
19
|
-
error: (event: {
|
|
20
|
-
type: TrackType;
|
|
21
|
-
error: DeviceError | null;
|
|
22
|
-
rawError: any;
|
|
23
|
-
}, state: ScreenShareDeviceState) => void;
|
|
24
|
-
};
|
|
25
|
-
export interface ScreenShareManagerConfig {
|
|
26
|
-
audioTrackConstraints?: boolean | MediaTrackConstraints;
|
|
27
|
-
videoTrackConstraints?: boolean | MediaTrackConstraints;
|
|
28
|
-
}
|
|
29
|
-
export type ScreenShareMedia = {
|
|
30
|
-
stream: MediaStream | null;
|
|
31
|
-
track: MediaStreamTrack | null;
|
|
32
|
-
enabled: boolean;
|
|
33
|
-
};
|
|
34
|
-
export type ScreenShareDeviceState = {
|
|
35
|
-
status: DevicesStatus;
|
|
36
|
-
audioMedia: ScreenShareMedia | null;
|
|
37
|
-
videoMedia: ScreenShareMedia | null;
|
|
38
|
-
error: DeviceError | null;
|
|
39
|
-
};
|
|
40
|
-
declare const ScreenShareManager_base: new () => TypedEmitter<DisplayMediaManagerEvents>;
|
|
41
|
-
export declare class ScreenShareManager extends ScreenShareManager_base {
|
|
42
|
-
private readonly defaultConfig?;
|
|
43
|
-
private config?;
|
|
44
|
-
private data;
|
|
45
|
-
getSnapshot(): ScreenShareDeviceState;
|
|
46
|
-
constructor(defaultConfig?: ScreenShareManagerConfig);
|
|
47
|
-
setConfig(config: ScreenShareManagerConfig): void;
|
|
48
|
-
private getType;
|
|
49
|
-
getMedia: () => ScreenShareMedia | null;
|
|
50
|
-
start(config?: ScreenShareManagerConfig): Promise<void>;
|
|
51
|
-
private setupOnEndedCallback;
|
|
52
|
-
private onTrackEnded;
|
|
53
|
-
stop(type: TrackType): Promise<void>;
|
|
54
|
-
setEnable(type: TrackType, value: boolean): void;
|
|
55
|
-
}
|
|
56
|
-
export {};
|
|
57
|
-
//# sourceMappingURL=ScreenShareManager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ScreenShareManager.d.ts","sourceRoot":"","sources":["../src/ScreenShareManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;AACjD,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,WAAW,CAAC;AAE3D,MAAM,MAAM,yBAAyB,GAAG;IACtC,WAAW,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACjF,aAAa,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACnF,aAAa,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACnF,cAAc,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,EAAE,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACpF,KAAK,EAAE,CACL,KAAK,EAAE;QACL,IAAI,EAAE,SAAS,CAAC;QAChB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;QAC1B,QAAQ,EAAE,GAAG,CAAC;KACf,EACD,KAAK,EAAE,sBAAsB,KAC1B,IAAI,CAAC;CACX,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,qBAAqB,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IACxD,qBAAqB,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;CACzD;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3B,CAAC;iDAEiE,YAAY,CAAC,yBAAyB,CAAC;AAA1G,qBAAa,kBAAmB,SAAQ,uBAAmE;IACzG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA2B;IAC1D,OAAO,CAAC,MAAM,CAAC,CAA2B;IAE1C,OAAO,CAAC,IAAI,CAKV;IAGK,WAAW,IAAI,sBAAsB;gBAIhC,aAAa,CAAC,EAAE,wBAAwB;IAK7C,SAAS,CAAC,MAAM,EAAE,wBAAwB;IAIjD,OAAO,CAAC,OAAO;IAOR,QAAQ,gCAA8B;IAEhC,KAAK,CAAC,MAAM,CAAC,EAAE,wBAAwB;IA0CpD,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,YAAY,CAKlB;IAEW,IAAI,CAAC,IAAI,EAAE,SAAS;IA2B1B,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO;CAwBjD"}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import EventEmitter from "events";
|
|
2
|
-
import { parseUserMediaError } from "./utils/errors";
|
|
3
|
-
export class ScreenShareManager extends EventEmitter {
|
|
4
|
-
defaultConfig;
|
|
5
|
-
config;
|
|
6
|
-
data = {
|
|
7
|
-
audioMedia: null,
|
|
8
|
-
videoMedia: null,
|
|
9
|
-
status: "Not requested",
|
|
10
|
-
error: null,
|
|
11
|
-
};
|
|
12
|
-
// todo add nested read only
|
|
13
|
-
getSnapshot() {
|
|
14
|
-
return this.data;
|
|
15
|
-
}
|
|
16
|
-
constructor(defaultConfig) {
|
|
17
|
-
super();
|
|
18
|
-
this.defaultConfig = defaultConfig;
|
|
19
|
-
}
|
|
20
|
-
setConfig(config) {
|
|
21
|
-
this.config = config;
|
|
22
|
-
}
|
|
23
|
-
getType(options) {
|
|
24
|
-
if (options.audio && options.video)
|
|
25
|
-
return "audiovideo";
|
|
26
|
-
if (options.audio)
|
|
27
|
-
return "audio";
|
|
28
|
-
if (options.video)
|
|
29
|
-
return "video";
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
getMedia = () => this.data.videoMedia;
|
|
33
|
-
async start(config) {
|
|
34
|
-
const options = {
|
|
35
|
-
video: config?.videoTrackConstraints ??
|
|
36
|
-
this.config?.videoTrackConstraints ??
|
|
37
|
-
this.defaultConfig?.videoTrackConstraints,
|
|
38
|
-
audio: config?.audioTrackConstraints ??
|
|
39
|
-
this.config?.audioTrackConstraints ??
|
|
40
|
-
this.defaultConfig?.audioTrackConstraints,
|
|
41
|
-
};
|
|
42
|
-
const type = this.getType(options);
|
|
43
|
-
if (!type)
|
|
44
|
-
return;
|
|
45
|
-
try {
|
|
46
|
-
const newStream = await navigator.mediaDevices.getDisplayMedia(options);
|
|
47
|
-
this.data = {
|
|
48
|
-
error: null,
|
|
49
|
-
videoMedia: {
|
|
50
|
-
enabled: true,
|
|
51
|
-
stream: newStream,
|
|
52
|
-
track: newStream?.getVideoTracks()[0] ?? null,
|
|
53
|
-
},
|
|
54
|
-
audioMedia: {
|
|
55
|
-
enabled: true,
|
|
56
|
-
stream: newStream,
|
|
57
|
-
track: newStream?.getAudioTracks()[0] ?? null,
|
|
58
|
-
},
|
|
59
|
-
status: "OK",
|
|
60
|
-
};
|
|
61
|
-
this.setupOnEndedCallback();
|
|
62
|
-
this.emit("deviceReady", { type: type }, this.data);
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
const parsedError = parseUserMediaError(error);
|
|
66
|
-
this.emit("error", { type, error: parsedError, rawError: error }, this.data);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
setupOnEndedCallback() {
|
|
70
|
-
if (this.data.videoMedia?.track) {
|
|
71
|
-
this.data.videoMedia.track.addEventListener("ended", async (event) => await this.onTrackEnded("video", event.target.id));
|
|
72
|
-
}
|
|
73
|
-
if (this.data.audioMedia?.track) {
|
|
74
|
-
this.data.audioMedia.track.addEventListener("ended", async (event) => await this.onTrackEnded("audio", event.target.id));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
onTrackEnded = async (kind, trackId) => {
|
|
78
|
-
const mediaType = kind === "video" ? "videoMedia" : "audioMedia";
|
|
79
|
-
if (trackId === this?.data[mediaType]?.track?.id) {
|
|
80
|
-
await this.stop("audiovideo");
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
async stop(type) {
|
|
84
|
-
if (type === "video") {
|
|
85
|
-
for (const track of this.data?.videoMedia?.stream?.getTracks() ?? []) {
|
|
86
|
-
track.stop();
|
|
87
|
-
}
|
|
88
|
-
this.data.videoMedia = null;
|
|
89
|
-
}
|
|
90
|
-
else if (type === "audio") {
|
|
91
|
-
// todo test it
|
|
92
|
-
for (const track of this.data?.audioMedia?.stream?.getTracks() ?? []) {
|
|
93
|
-
track.stop();
|
|
94
|
-
}
|
|
95
|
-
this.data.audioMedia = null;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
for (const track of this.data?.videoMedia?.stream?.getTracks() ?? []) {
|
|
99
|
-
track.stop();
|
|
100
|
-
}
|
|
101
|
-
this.data.videoMedia = null;
|
|
102
|
-
for (const track of this.data?.audioMedia?.stream?.getTracks() ?? []) {
|
|
103
|
-
track.stop();
|
|
104
|
-
}
|
|
105
|
-
this.data.audioMedia = null;
|
|
106
|
-
}
|
|
107
|
-
this.emit("deviceStopped", { type }, this.data);
|
|
108
|
-
}
|
|
109
|
-
setEnable(type, value) {
|
|
110
|
-
if (type === "video" && this.data.videoMedia?.track) {
|
|
111
|
-
this.data.videoMedia.track.enabled = value;
|
|
112
|
-
this.data.videoMedia.enabled = value;
|
|
113
|
-
}
|
|
114
|
-
else if (type === "audio" && this.data.audioMedia?.track) {
|
|
115
|
-
this.data.audioMedia.track.enabled = value;
|
|
116
|
-
this.data.audioMedia.enabled = value;
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
if (this.data.videoMedia?.track) {
|
|
120
|
-
this.data.videoMedia.track.enabled = value;
|
|
121
|
-
this.data.videoMedia.enabled = value;
|
|
122
|
-
}
|
|
123
|
-
if (this.data.audioMedia?.track) {
|
|
124
|
-
this.data.audioMedia.track.enabled = value;
|
|
125
|
-
this.data.audioMedia.enabled = value;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (value) {
|
|
129
|
-
this.emit("deviceEnabled", { type }, this.data);
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
this.emit("deviceDisabled", { type }, this.data);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|