@fishjam-cloud/react-client 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/DeviceManager.d.ts +25 -15
  2. package/dist/DeviceManager.d.ts.map +1 -1
  3. package/dist/DeviceManager.js +125 -79
  4. package/dist/devices/MiddlewareManager.d.ts +9 -0
  5. package/dist/devices/MiddlewareManager.d.ts.map +1 -0
  6. package/dist/devices/MiddlewareManager.js +26 -0
  7. package/dist/fishjamProvider.d.ts +5 -2
  8. package/dist/fishjamProvider.d.ts.map +1 -1
  9. package/dist/fishjamProvider.js +12 -4
  10. package/dist/hooks/deviceManagers/useDeviceManager.d.ts.map +1 -1
  11. package/dist/hooks/deviceManagers/useDeviceManager.js +2 -2
  12. package/dist/hooks/devices/useCamera.d.ts +7 -0
  13. package/dist/hooks/devices/useCamera.d.ts.map +1 -0
  14. package/dist/hooks/devices/useCamera.js +35 -0
  15. package/dist/hooks/devices/useInitializeDevices.d.ts +8 -0
  16. package/dist/hooks/devices/useInitializeDevices.d.ts.map +1 -0
  17. package/dist/hooks/devices/useInitializeDevices.js +41 -0
  18. package/dist/hooks/devices/useMicrophone.d.ts +7 -0
  19. package/dist/hooks/devices/useMicrophone.d.ts.map +1 -0
  20. package/dist/hooks/devices/useMicrophone.js +37 -0
  21. package/dist/hooks/public.d.ts +3 -1
  22. package/dist/hooks/public.d.ts.map +1 -1
  23. package/dist/hooks/public.js +3 -1
  24. package/dist/hooks/useFishjamContext.d.ts +4 -3
  25. package/dist/hooks/useFishjamContext.d.ts.map +1 -1
  26. package/dist/hooks/useScreenShare.d.ts +10 -1
  27. package/dist/hooks/useScreenShare.d.ts.map +1 -1
  28. package/dist/hooks/useScreenShare.js +34 -33
  29. package/dist/hooks/useTrackManager.d.ts +4 -2
  30. package/dist/hooks/useTrackManager.d.ts.map +1 -1
  31. package/dist/hooks/useTrackManager.js +55 -71
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/mediaInitializer.d.ts.map +1 -1
  35. package/dist/mediaInitializer.js +30 -1
  36. package/dist/types/internal.d.ts +20 -31
  37. package/dist/types/internal.d.ts.map +1 -1
  38. package/dist/types/public.d.ts +16 -5
  39. package/dist/types/public.d.ts.map +1 -1
  40. package/dist/utils/bandwidth.d.ts +3 -0
  41. package/dist/utils/bandwidth.d.ts.map +1 -0
  42. package/dist/utils/bandwidth.js +4 -0
  43. package/dist/utils/localStorage.d.ts +3 -4
  44. package/dist/utils/localStorage.d.ts.map +1 -1
  45. package/dist/utils/localStorage.js +18 -4
  46. package/dist/utils/track.d.ts +8 -2
  47. package/dist/utils/track.d.ts.map +1 -1
  48. package/dist/utils/track.js +23 -0
  49. package/package.json +5 -5
  50. package/dist/hooks/deviceManagers/useAudioDeviceManager.d.ts +0 -6
  51. package/dist/hooks/deviceManagers/useAudioDeviceManager.d.ts.map +0 -1
  52. package/dist/hooks/deviceManagers/useAudioDeviceManager.js +0 -11
  53. package/dist/hooks/deviceManagers/useVideoDeviceManager.d.ts +0 -6
  54. package/dist/hooks/deviceManagers/useVideoDeviceManager.d.ts.map +0 -1
  55. package/dist/hooks/deviceManagers/useVideoDeviceManager.js +0 -11
  56. package/dist/hooks/useDevices.d.ts +0 -19
  57. package/dist/hooks/useDevices.d.ts.map +0 -1
  58. package/dist/hooks/useDevices.js +0 -98
  59. package/dist/hooks/useProcessedPreviewStream.d.ts +0 -3
  60. package/dist/hooks/useProcessedPreviewStream.d.ts.map +0 -1
  61. package/dist/hooks/useProcessedPreviewStream.js +0 -17
  62. package/dist/utils/media.d.ts +0 -13
  63. package/dist/utils/media.d.ts.map +0 -1
  64. package/dist/utils/media.js +0 -64
@@ -1,6 +1,7 @@
1
- import type { DeviceState, MediaManager, DeviceError, DeviceManagerStatus } from "./types/internal";
1
+ import type { DeviceError, DeviceManagerStatus, DeviceState, Media, MediaManager } from "./types/internal";
2
2
  import type TypedEmitter from "typed-emitter";
3
- import type { PersistLastDeviceHandlers } from "./types/public";
3
+ import type { PersistLastDeviceHandlers, TrackMiddleware } from "./types/public";
4
+ export type DeviceType = "audio" | "video";
4
5
  export type DeviceManagerEvents = {
5
6
  managerStarted: (event: DeviceState & {
6
7
  constraints: MediaTrackConstraints | undefined;
@@ -19,10 +20,11 @@ export type DeviceManagerEvents = {
19
20
  deviceStopped: (state: DeviceState) => void;
20
21
  deviceEnabled: (state: DeviceState) => void;
21
22
  deviceDisabled: (state: DeviceState) => void;
23
+ middlewareSet: (state: DeviceState) => void;
22
24
  error: (event: any, state: DeviceState) => void;
23
25
  };
24
26
  export type DeviceManagerConfig = {
25
- deviceType: "audio" | "video";
27
+ deviceType: DeviceType;
26
28
  defaultConstraints: MediaTrackConstraints;
27
29
  userConstraints?: boolean | MediaTrackConstraints;
28
30
  storage?: boolean | PersistLastDeviceHandlers;
@@ -30,26 +32,34 @@ export type DeviceManagerConfig = {
30
32
  declare const DeviceManager_base: new () => TypedEmitter<DeviceManagerEvents>;
31
33
  export declare class DeviceManager extends DeviceManager_base implements MediaManager {
32
34
  private readonly constraints;
33
- private readonly storageConfig;
34
35
  private status;
35
36
  private readonly deviceType;
36
- deviceState: DeviceState;
37
+ private readonly middlewareManager;
38
+ readonly getLastDevice: () => MediaDeviceInfo | null;
39
+ private readonly saveLastDevice;
40
+ private rawMedia;
41
+ private processedMediaTrack;
42
+ private media;
43
+ private mediaStatus;
44
+ private devices;
45
+ private devicesStatus;
46
+ private error;
37
47
  constructor({ deviceType, storage, userConstraints, defaultConstraints }: DeviceManagerConfig);
38
- private createStorageConfig;
48
+ getState(): DeviceState;
39
49
  getStatus(): DeviceManagerStatus;
40
50
  getConstraints(): MediaTrackConstraints | boolean;
41
- getMedia: () => import("./types/internal").Media | null;
42
- getTracks: () => MediaStreamTrack[];
43
- initialize: (stream: MediaStream | null, track: MediaStreamTrack | null, devices: MediaDeviceInfo[], requestedMedia: boolean, error?: DeviceError | null) => DeviceState;
44
- private setupOnEndedCallback;
45
- private onTrackEnded;
46
- getLastDevice(): MediaDeviceInfo | null;
47
- private saveLastDevice;
51
+ getDeviceType: () => DeviceType;
52
+ getMedia: () => Media | null;
53
+ initialize: (stream: MediaStream | null, track: MediaStreamTrack | null, devices: MediaDeviceInfo[], requestedMedia: boolean, error?: DeviceError | null) => void;
48
54
  start(deviceId?: string): Promise<void>;
49
- stop(): Promise<void>;
55
+ setTrackMiddleware(middleware: TrackMiddleware | null): Promise<void>;
56
+ getMiddleware(): TrackMiddleware | null;
57
+ stop(): void;
50
58
  disable(): void;
51
59
  enable(): void;
52
- getDeviceType: () => "audio" | "video";
60
+ private setEnable;
61
+ private updateMedia;
62
+ private setCurrentMedia;
53
63
  }
54
64
  export {};
55
65
  //# sourceMappingURL=DeviceManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceManager.d.ts","sourceRoot":"","sources":["../src/DeviceManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAKpG,OAAO,KAAK,YAAY,MAAM,eAAe,CAAC;AAG9C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,CACd,KAAK,EAAE,WAAW,GAAG;QACnB,WAAW,EAAE,qBAAqB,GAAG,SAAS,CAAC;KAChD,EACD,KAAK,EAAE,WAAW,KACf,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,CACd,KAAK,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAA;KAAE,EAC7E,KAAK,EAAE,WAAW,KACf,IAAI,CAAC;IACV,WAAW,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1E,YAAY,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACxF,aAAa,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5C,aAAa,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAE7C,KAAK,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC;IAC9B,kBAAkB,EAAE,qBAAqB,CAAC;IAC1C,eAAe,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;CAC/C,CAAC;4CAGmC,YAAY,CAAC,mBAAmB,CAAC;AADtE,qBAAa,aACX,SAAQ,kBACR,YAAW,YAAY;IAEvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmC;IAEjE,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAExC,WAAW,EAAE,WAAW,CAM7B;gBAEU,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,EAAE,mBAAmB;IAO7F,OAAO,CAAC,mBAAmB;IASpB,SAAS,IAAI,mBAAmB;IAIhC,cAAc,IAAI,qBAAqB,GAAG,OAAO;IAIjD,QAAQ,gDAAgC;IAExC,SAAS,2BAMd;IAEK,UAAU,WACP,WAAW,GAAG,IAAI,SACnB,gBAAgB,GAAG,IAAI,WACrB,eAAe,EAAE,kBACV,OAAO,UAChB,WAAW,GAAG,IAAI,iBAazB;IAEF,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,YAAY,CAIlB;IAEK,aAAa,IAAI,eAAe,GAAG,IAAI;IAI9C,OAAO,CAAC,cAAc;IAIT,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM;IAgFvB,IAAI;IAOV,OAAO;IAWP,MAAM;IAWN,aAAa,0BAElB;CACH"}
1
+ {"version":3,"file":"DeviceManager.d.ts","sourceRoot":"","sources":["../src/DeviceManager.ts"],"names":[],"mappings":"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"}
@@ -1,31 +1,43 @@
1
1
  import { prepareMediaTrackConstraints } from "./constraints";
2
2
  import EventEmitter from "events";
3
- import { getDeviceInfo, getLocalStorageConfig, prepareDeviceState } from "./utils/media";
3
+ import { MiddlewareManager } from "./devices/MiddlewareManager";
4
+ import { createStorageConfig } from "./utils/localStorage";
5
+ import { setupOnEndedCallback } from "./utils/track";
4
6
  import { parseUserMediaError } from "./utils/errors";
7
+ const NOOP = () => { };
5
8
  export class DeviceManager extends EventEmitter {
6
9
  constraints;
7
- storageConfig;
8
10
  status = "uninitialized";
9
11
  deviceType;
10
- deviceState = {
11
- media: null,
12
- mediaStatus: "Not requested",
13
- devices: null,
14
- devicesStatus: "Not requested",
15
- error: null,
16
- };
12
+ middlewareManager = new MiddlewareManager();
13
+ getLastDevice = () => null;
14
+ saveLastDevice = NOOP;
15
+ rawMedia = null;
16
+ processedMediaTrack = null;
17
+ media = null;
18
+ mediaStatus = "Not requested";
19
+ devices = null;
20
+ devicesStatus = "Not requested";
21
+ error = null;
17
22
  constructor({ deviceType, storage, userConstraints, defaultConstraints }) {
18
23
  super();
19
- this.storageConfig = this.createStorageConfig(deviceType, storage);
24
+ const storageConfig = createStorageConfig(deviceType, storage);
25
+ if (storageConfig) {
26
+ this.getLastDevice = storageConfig.getLastDevice;
27
+ this.saveLastDevice = storageConfig.saveLastDevice;
28
+ }
20
29
  this.deviceType = deviceType;
21
30
  this.constraints = userConstraints ?? defaultConstraints;
22
31
  }
23
- createStorageConfig(deviceType, storage) {
24
- if (storage === false)
25
- return null;
26
- if (storage === true || storage === undefined)
27
- return getLocalStorageConfig(deviceType);
28
- return storage;
32
+ getState() {
33
+ return {
34
+ mediaStatus: this.mediaStatus,
35
+ devices: this.devices,
36
+ devicesStatus: this.devicesStatus,
37
+ error: this.error,
38
+ media: this.media,
39
+ currentMiddleware: this.middlewareManager.getMiddleware(),
40
+ };
29
41
  }
30
42
  getStatus() {
31
43
  return this.status;
@@ -33,60 +45,49 @@ export class DeviceManager extends EventEmitter {
33
45
  getConstraints() {
34
46
  return this.constraints;
35
47
  }
36
- getMedia = () => this.deviceState.media;
37
- getTracks = () => {
38
- const stream = this.deviceState.media?.stream;
39
- if (this.deviceType === "audio") {
40
- return stream?.getAudioTracks() ?? [];
41
- }
42
- return stream?.getVideoTracks() ?? [];
48
+ getDeviceType = () => {
49
+ return this.deviceType;
50
+ };
51
+ getMedia = () => {
52
+ return this.media;
43
53
  };
44
54
  initialize = (stream, track, devices, requestedMedia, error = null) => {
45
- this.deviceState = prepareDeviceState(stream, track, devices, error, requestedMedia);
46
- const deviceInfo = this.deviceState.media?.deviceInfo;
55
+ const deviceInfo = getDeviceInfo(track?.getSettings()?.deviceId || null, devices);
56
+ const [devicesStatus, newError] = prepareStatus(requestedMedia, track);
57
+ this.devices = devices;
58
+ this.devicesStatus = devicesStatus;
59
+ this.mediaStatus = devicesStatus;
60
+ this.error = newError ?? error;
61
+ this.updateMedia({
62
+ stream: track ? stream : null,
63
+ track,
64
+ deviceInfo,
65
+ enabled: Boolean(track?.enabled),
66
+ });
47
67
  if (deviceInfo)
48
- this.saveLastDevice(deviceInfo);
68
+ this.saveLastDevice?.(deviceInfo);
49
69
  this.status = "initialized";
50
- this.setupOnEndedCallback();
51
- this.emit("managerInitialized", this.deviceState);
52
- return this.deviceState;
53
- };
54
- setupOnEndedCallback() {
55
- if (this.deviceState?.media?.track) {
56
- this.deviceState.media.track.addEventListener("ended", async (event) => await this.onTrackEnded(event.target.id));
57
- }
58
- }
59
- onTrackEnded = async (trackId) => {
60
- if (trackId === this?.deviceState.media?.track?.id) {
61
- await this.stop();
70
+ if (track) {
71
+ setupOnEndedCallback(track, () => this?.rawMedia?.track?.id, async () => this.stop());
62
72
  }
73
+ this.emit("managerInitialized", this.getState());
63
74
  };
64
- getLastDevice() {
65
- return this.storageConfig?.getLastDevice?.() ?? null;
66
- }
67
- saveLastDevice(info) {
68
- this.storageConfig?.saveLastDevice(info);
69
- }
70
75
  async start(deviceId) {
71
- const newDeviceId = deviceId ?? this.getLastDevice()?.deviceId;
72
- const currentDeviceId = this.deviceState.media?.deviceInfo?.deviceId;
76
+ const newDeviceId = deviceId ?? this.getLastDevice?.()?.deviceId;
77
+ const currentDeviceId = this.rawMedia?.deviceInfo?.deviceId;
73
78
  const shouldReplaceDevice = Boolean(currentDeviceId && currentDeviceId !== newDeviceId);
74
79
  const isDeviceStopped = currentDeviceId === undefined;
75
80
  const shouldProceed = isDeviceStopped || shouldReplaceDevice;
76
81
  const exactConstraints = shouldProceed && prepareMediaTrackConstraints(newDeviceId, this.constraints);
77
82
  if (!exactConstraints)
78
83
  return;
79
- this.deviceState.mediaStatus = "Requesting";
80
- this.emit("devicesStarted", { ...this.deviceState, restarting: shouldReplaceDevice, constraints: exactConstraints }, this.deviceState);
84
+ this.mediaStatus = "Requesting";
85
+ this.emit("devicesStarted", { restarting: shouldReplaceDevice, constraints: exactConstraints }, this.getState());
81
86
  try {
82
87
  const stream = await navigator.mediaDevices.getUserMedia({ [this.deviceType]: exactConstraints });
83
- const getTrack = () => {
84
- const tracks = this.deviceType === "audio" ? stream.getAudioTracks() : stream.getVideoTracks();
85
- return tracks[0] ?? null;
86
- };
87
- const track = getTrack();
88
+ const track = getTrack(stream, this.deviceType);
88
89
  const currentDeviceId = track?.getSettings()?.deviceId;
89
- const deviceInfo = currentDeviceId ? getDeviceInfo(currentDeviceId, this.deviceState.devices ?? []) : null;
90
+ const deviceInfo = currentDeviceId ? getDeviceInfo(currentDeviceId, this.devices ?? []) : null;
90
91
  if (deviceInfo) {
91
92
  this.saveLastDevice?.(deviceInfo);
92
93
  }
@@ -103,17 +104,20 @@ export class DeviceManager extends EventEmitter {
103
104
  // The ended event in Safari has already been emitted and will be handled in the future.
104
105
  // Therefore, in the `onTrackEnded` method, events for already stopped tracks are filtered out to prevent the state from being damaged.
105
106
  if (shouldReplaceDevice) {
106
- this.deviceState?.media?.track?.stop();
107
+ this.rawMedia?.track?.stop();
108
+ this.processedMediaTrack?.stop();
107
109
  }
108
- this.deviceState.media = {
110
+ this.updateMedia({
109
111
  stream,
110
112
  track,
111
113
  deviceInfo,
112
- enabled: true,
113
- };
114
- this.setupOnEndedCallback();
115
- this.deviceState.mediaStatus = "OK";
116
- this.emit("devicesReady", { ...this.deviceState, restarted: shouldReplaceDevice }, this.deviceState);
114
+ enabled: Boolean(track?.enabled),
115
+ });
116
+ if (track) {
117
+ setupOnEndedCallback(track, () => this?.rawMedia?.track?.id, async () => this.stop());
118
+ }
119
+ this.mediaStatus = "OK";
120
+ this.emit("devicesReady", { ...this.getState(), restarted: shouldReplaceDevice }, this.getState());
117
121
  }
118
122
  catch (err) {
119
123
  const parsedError = parseUserMediaError(err);
@@ -122,33 +126,75 @@ export class DeviceManager extends EventEmitter {
122
126
  constraints: exactConstraints,
123
127
  };
124
128
  if (exactConstraints) {
125
- this.deviceState.error = parsedError;
129
+ this.error = parsedError;
126
130
  }
127
- this.emit("error", event, this.deviceState);
131
+ this.emit("error", event, this.getState());
128
132
  }
129
133
  }
130
- async stop() {
131
- this.deviceState.media?.track?.stop();
132
- this.deviceState.media = null;
133
- this.emit("deviceStopped", this.deviceState);
134
+ async setTrackMiddleware(middleware) {
135
+ const rawTrack = this.rawMedia?.track ?? null;
136
+ const processedMedia = this.middlewareManager.applyMiddleware(rawTrack, middleware);
137
+ this.setCurrentMedia(this.rawMedia, processedMedia);
138
+ this.emit("middlewareSet", this.getState());
139
+ }
140
+ getMiddleware() {
141
+ return this.middlewareManager.getMiddleware();
142
+ }
143
+ stop() {
144
+ this.middlewareManager.clearMiddleware();
145
+ this.processedMediaTrack?.stop();
146
+ this.rawMedia?.track?.stop();
147
+ this.updateMedia(null);
148
+ this.emit("deviceStopped", this.getState());
134
149
  }
135
150
  disable() {
136
- if (!this.deviceState.media || !this.deviceState.media?.track) {
137
- return;
151
+ this.setEnable(this.rawMedia?.track ?? null, false);
152
+ this.setEnable(this.processedMediaTrack, false);
153
+ if (this.media) {
154
+ this.media.enabled = false;
138
155
  }
139
- this.deviceState.media.track.enabled = false;
140
- this.deviceState.media.enabled = false;
141
- this.emit("deviceDisabled", this.deviceState);
156
+ this.emit("deviceDisabled", this.getState());
142
157
  }
143
158
  enable() {
144
- if (!this.deviceState.media || !this.deviceState.media?.track) {
145
- return;
159
+ this.setEnable(this.rawMedia?.track ?? null, true);
160
+ this.setEnable(this.processedMediaTrack, true);
161
+ if (this.media) {
162
+ this.media.enabled = true;
146
163
  }
147
- this.deviceState.media.track.enabled = true;
148
- this.deviceState.media.enabled = true;
149
- this.emit("deviceEnabled", this.deviceState);
164
+ this.emit("deviceEnabled", this.getState());
150
165
  }
151
- getDeviceType = () => {
152
- return this.deviceType;
166
+ setEnable = (track, value) => {
167
+ if (!track)
168
+ return;
169
+ track.enabled = value;
153
170
  };
171
+ updateMedia(media) {
172
+ this.rawMedia = !media ? null : { ...media };
173
+ const processedMedia = this.middlewareManager.applyMiddleware(media?.track ?? null);
174
+ this.setCurrentMedia(this.rawMedia, processedMedia);
175
+ }
176
+ setCurrentMedia(media, processedTrack) {
177
+ this.processedMediaTrack = processedTrack;
178
+ const streamTrack = processedTrack ? { track: processedTrack, stream: new MediaStream([processedTrack]) } : media;
179
+ this.media = streamTrack
180
+ ? {
181
+ ...streamTrack,
182
+ enabled: Boolean(streamTrack.track?.enabled),
183
+ deviceInfo: this.rawMedia?.deviceInfo ?? null,
184
+ }
185
+ : null;
186
+ }
187
+ }
188
+ function getDeviceInfo(trackDeviceId, devices) {
189
+ return (trackDeviceId && devices.find(({ deviceId }) => trackDeviceId === deviceId)) || null;
190
+ }
191
+ function getTrack(stream, deviceType) {
192
+ return (deviceType === "audio" ? stream?.getAudioTracks()?.[0] : stream?.getVideoTracks()?.[0]) ?? null;
193
+ }
194
+ function prepareStatus(requested, track) {
195
+ if (!requested)
196
+ return ["Not requested", null];
197
+ if (track)
198
+ return ["OK", null];
199
+ return ["Error", null];
154
200
  }
@@ -0,0 +1,9 @@
1
+ import type { TrackMiddleware } from "../types/public";
2
+ export declare class MiddlewareManager {
3
+ private middleware;
4
+ private clearMiddlewareFn;
5
+ getMiddleware(): TrackMiddleware | null;
6
+ clearMiddleware(): void;
7
+ applyMiddleware(rawTrack: MediaStreamTrack | null, middleware?: TrackMiddleware | null): MediaStreamTrack | null;
8
+ }
9
+ //# sourceMappingURL=MiddlewareManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MiddlewareManager.d.ts","sourceRoot":"","sources":["../../src/devices/MiddlewareManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,iBAAiB,CAA6B;IAE/C,aAAa,IAAI,eAAe,GAAG,IAAI;IAIvC,eAAe;IAKf,eAAe,CACpB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,EACjC,UAAU,GAAE,eAAe,GAAG,IAAsB,GACnD,gBAAgB,GAAG,IAAI;CAmB3B"}
@@ -0,0 +1,26 @@
1
+ import { setupOnEndedCallback } from "../utils/track";
2
+ export class MiddlewareManager {
3
+ middleware = null;
4
+ clearMiddlewareFn = null;
5
+ getMiddleware() {
6
+ return this.middleware;
7
+ }
8
+ clearMiddleware() {
9
+ this.clearMiddlewareFn?.();
10
+ this.clearMiddlewareFn = null;
11
+ }
12
+ applyMiddleware(rawTrack, middleware = this.middleware) {
13
+ this.middleware = middleware;
14
+ this.clearMiddleware();
15
+ if (!rawTrack || !middleware)
16
+ return null;
17
+ const { onClear, track } = middleware(rawTrack);
18
+ if (onClear)
19
+ this.clearMiddlewareFn = onClear;
20
+ setupOnEndedCallback(track, () => track.id, async () => {
21
+ this.clearMiddleware();
22
+ track.stop();
23
+ });
24
+ return track;
25
+ }
26
+ }
@@ -1,14 +1,17 @@
1
1
  import { type PropsWithChildren } from "react";
2
2
  import { type ReconnectConfig } from "@fishjam-cloud/ts-client";
3
- import type { PersistLastDeviceHandlers } from "./types/public";
3
+ import type { BandwidthLimits, PersistLastDeviceHandlers, StartStreamingProps } from "./types/public";
4
4
  interface FishjamProviderProps extends PropsWithChildren {
5
5
  reconnect?: ReconnectConfig | boolean;
6
6
  constraints?: Pick<MediaStreamConstraints, "audio" | "video">;
7
7
  persistLastDevice?: boolean | PersistLastDeviceHandlers;
8
+ bandwidthLimits?: Partial<BandwidthLimits>;
9
+ autoStreamCamera?: StartStreamingProps | false;
10
+ autoStreamMicrophone?: StartStreamingProps | false;
8
11
  }
9
12
  /**
10
13
  * @category Components
11
14
  */
12
- export declare function FishjamProvider({ children, reconnect, constraints, persistLastDevice }: FishjamProviderProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function FishjamProvider({ children, reconnect, constraints, persistLastDevice, bandwidthLimits, autoStreamCamera, autoStreamMicrophone, }: FishjamProviderProps): import("react/jsx-runtime").JSX.Element;
13
16
  export {};
14
17
  //# sourceMappingURL=fishjamProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fishjamProvider.d.ts","sourceRoot":"","sources":["../src/fishjamProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAGjE,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAO/E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAEhE,UAAU,oBAAqB,SAAQ,iBAAiB;IACtD,SAAS,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC;IACtC,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;IAC9D,iBAAiB,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;CACzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,EAAE,oBAAoB,2CAsD5G"}
1
+ {"version":3,"file":"fishjamProvider.d.ts","sourceRoot":"","sources":["../src/fishjamProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAGvD,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAO/E,OAAO,KAAK,EAAE,eAAe,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAItG,UAAU,oBAAqB,SAAQ,iBAAiB;IACtD,SAAS,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC;IACtC,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;IAC9D,iBAAiB,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;IACxD,eAAe,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3C,gBAAgB,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;IAC/C,oBAAoB,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;CACpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,GACrB,EAAE,oBAAoB,2CA8DtB"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRef, useState } from "react";
2
+ import { useRef } from "react";
3
3
  import { useTrackManager } from "./hooks/useTrackManager";
4
4
  import { FishjamClient } from "@fishjam-cloud/ts-client";
5
5
  import { FishjamContext } from "./hooks/useFishjamContext";
@@ -7,10 +7,12 @@ import { DeviceManager } from "./DeviceManager";
7
7
  import { usePeerStatus } from "./hooks/usePeerStatus";
8
8
  import { useFishjamClientState } from "./hooks/useFishjamClientState";
9
9
  import { AUDIO_TRACK_CONSTRAINTS, VIDEO_TRACK_CONSTRAINTS } from "./constraints";
10
+ import { useScreenShareManager } from "./hooks/useScreenShare";
11
+ import { mergeWithDefaultBandwitdthLimits } from "./utils/bandwidth";
10
12
  /**
11
13
  * @category Components
12
14
  */
13
- export function FishjamProvider({ children, reconnect, constraints, persistLastDevice }) {
15
+ export function FishjamProvider({ children, reconnect, constraints, persistLastDevice, bandwidthLimits, autoStreamCamera, autoStreamMicrophone, }) {
14
16
  const fishjamClientRef = useRef(new FishjamClient({ reconnect }));
15
17
  const hasDevicesBeenInitializedRef = useRef(false);
16
18
  const storage = persistLastDevice;
@@ -26,29 +28,35 @@ export function FishjamProvider({ children, reconnect, constraints, persistLastD
26
28
  userConstraints: constraints?.audio,
27
29
  storage,
28
30
  }));
29
- const screenShareState = useState({ stream: null, trackIds: null });
30
31
  const { peerStatus, getCurrentPeerStatus } = usePeerStatus(fishjamClientRef.current);
32
+ const mergedBandwidthLimits = mergeWithDefaultBandwitdthLimits(bandwidthLimits);
31
33
  const videoTrackManager = useTrackManager({
32
34
  mediaManager: videoDeviceManagerRef.current,
33
35
  tsClient: fishjamClientRef.current,
34
36
  getCurrentPeerStatus,
37
+ bandwidthLimits: mergedBandwidthLimits,
38
+ autoStreamProps: autoStreamCamera,
35
39
  });
36
40
  const audioTrackManager = useTrackManager({
37
41
  mediaManager: audioDeviceManagerRef.current,
38
42
  tsClient: fishjamClientRef.current,
39
43
  getCurrentPeerStatus,
44
+ bandwidthLimits: mergedBandwidthLimits,
45
+ autoStreamProps: autoStreamMicrophone,
40
46
  });
47
+ const screenShareManager = useScreenShareManager({ fishjamClient: fishjamClientRef.current, getCurrentPeerStatus });
41
48
  const clientState = useFishjamClientState(fishjamClientRef.current);
42
49
  const context = {
43
50
  fishjamClientRef,
44
51
  peerStatus,
45
- screenShareState,
52
+ screenShareManager,
46
53
  videoTrackManager,
47
54
  audioTrackManager,
48
55
  videoDeviceManagerRef,
49
56
  audioDeviceManagerRef,
50
57
  hasDevicesBeenInitializedRef,
51
58
  clientState,
59
+ bandwidthLimits: mergedBandwidthLimits,
52
60
  };
53
61
  return _jsx(FishjamContext.Provider, { value: context, children: children });
54
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useDeviceManager.d.ts","sourceRoot":"","sources":["../../../src/hooks/deviceManagers/useDeviceManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,qBAAqB,CAAC;AAc9E,eAAO,MAAM,gBAAgB,kBAAmB,aAAa,uBAoC5D,CAAC"}
1
+ {"version":3,"file":"useDeviceManager.d.ts","sourceRoot":"","sources":["../../../src/hooks/deviceManagers/useDeviceManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,qBAAqB,CAAC;AAe9E,eAAO,MAAM,gBAAgB,kBAAmB,aAAa,uBAmC5D,CAAC"}
@@ -9,6 +9,7 @@ const eventNames = [
9
9
  "devicesReady",
10
10
  "managerInitialized",
11
11
  "error",
12
+ "middlewareSet",
12
13
  ];
13
14
  export const useDeviceManager = (deviceManager) => {
14
15
  const mutationRef = useRef(false);
@@ -26,9 +27,8 @@ export const useDeviceManager = (deviceManager) => {
26
27
  const getSnapshot = useCallback(() => {
27
28
  if (mutationRef.current || lastSnapshotRef.current === null) {
28
29
  lastSnapshotRef.current = {
29
- deviceState: deviceManager.deviceState,
30
+ deviceState: deviceManager.getState(),
30
31
  status: deviceManager.getStatus(),
31
- tracks: deviceManager.getTracks(),
32
32
  };
33
33
  mutationRef.current = false;
34
34
  }
@@ -0,0 +1,7 @@
1
+ import type { Device } from "../../types/public";
2
+ /**
3
+ *
4
+ * @category Devices
5
+ */
6
+ export declare function useCamera(): Device;
7
+ //# sourceMappingURL=useCamera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCamera.d.ts","sourceRoot":"","sources":["../../../src/hooks/devices/useCamera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAIjD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CA+BlC"}
@@ -0,0 +1,35 @@
1
+ import { useDeviceManager } from "../deviceManagers/useDeviceManager";
2
+ import { useFishjamContext } from "../useFishjamContext";
3
+ /**
4
+ *
5
+ * @category Devices
6
+ */
7
+ export function useCamera() {
8
+ const { videoTrackManager, videoDeviceManagerRef } = useFishjamContext();
9
+ const { deviceState, status } = useDeviceManager(videoDeviceManagerRef.current);
10
+ const { currentTrack, ...trackManager } = videoTrackManager;
11
+ const stream = deviceState.media?.stream ?? null;
12
+ const currentMiddleware = deviceState.currentMiddleware ?? null;
13
+ const isStreaming = Boolean(currentTrack?.stream);
14
+ const track = stream?.getAudioTracks()[0] ?? null;
15
+ const trackId = currentTrack?.trackId ?? null;
16
+ const devices = deviceState.devices ?? [];
17
+ const activeDevice = deviceState.media?.deviceInfo ?? null;
18
+ const isMuted = !deviceState.media?.enabled;
19
+ const deviceError = deviceState.error ?? null;
20
+ const isDeviceEnabled = Boolean(deviceState.media);
21
+ return {
22
+ ...trackManager,
23
+ currentMiddleware,
24
+ status,
25
+ stream,
26
+ track,
27
+ devices,
28
+ activeDevice,
29
+ deviceError,
30
+ isMuted,
31
+ isStreaming,
32
+ trackId,
33
+ isDeviceEnabled,
34
+ };
35
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ *
3
+ * @category Devices
4
+ */
5
+ export declare const useInitializeDevices: () => {
6
+ initializeDevices: () => Promise<void>;
7
+ };
8
+ //# sourceMappingURL=useInitializeDevices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInitializeDevices.d.ts","sourceRoot":"","sources":["../../../src/hooks/devices/useInitializeDevices.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;CAqDhC,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { useCallback } from "react";
2
+ import { prepareConstraints } from "../../constraints";
3
+ import { getAvailableMedia, getCorrectedResult } from "../../mediaInitializer";
4
+ import { useFishjamContext } from "../useFishjamContext";
5
+ /**
6
+ *
7
+ * @category Devices
8
+ */
9
+ export const useInitializeDevices = () => {
10
+ const { videoDeviceManagerRef, audioDeviceManagerRef, hasDevicesBeenInitializedRef } = useFishjamContext();
11
+ const initializeDevices = useCallback(async () => {
12
+ if (hasDevicesBeenInitializedRef.current)
13
+ return;
14
+ hasDevicesBeenInitializedRef.current = true;
15
+ const videoManager = videoDeviceManagerRef.current;
16
+ const audioManager = audioDeviceManagerRef.current;
17
+ const constraints = {
18
+ video: videoManager.getConstraints(),
19
+ audio: audioManager.getConstraints(),
20
+ };
21
+ const previousDevices = {
22
+ video: videoManager.getLastDevice(),
23
+ audio: audioManager.getLastDevice(),
24
+ };
25
+ // Attempt to start the last selected device to avoid an unnecessary restart.
26
+ // Without this, the first device will start, and `getCorrectedResult` will attempt to fix it.
27
+ let [stream, deviceErrors] = await getAvailableMedia({
28
+ video: prepareConstraints(previousDevices.video?.deviceId, constraints.video),
29
+ audio: prepareConstraints(previousDevices.audio?.deviceId, constraints.audio),
30
+ });
31
+ const devices = await navigator.mediaDevices.enumerateDevices();
32
+ const videoDevices = devices.filter(({ kind }) => kind === "videoinput");
33
+ const audioDevices = devices.filter(({ kind }) => kind === "audioinput");
34
+ if (stream) {
35
+ [stream, deviceErrors] = await getCorrectedResult(stream, deviceErrors, devices, constraints, previousDevices);
36
+ }
37
+ videoManager.initialize(stream, stream?.getVideoTracks()?.[0] ?? null, videoDevices, !!constraints.video, deviceErrors.video);
38
+ audioManager.initialize(stream, stream?.getAudioTracks()?.[0] ?? null, audioDevices, !!constraints.audio, deviceErrors.audio);
39
+ }, [videoDeviceManagerRef, audioDeviceManagerRef, hasDevicesBeenInitializedRef]);
40
+ return { initializeDevices };
41
+ };
@@ -0,0 +1,7 @@
1
+ import type { AudioDevice } from "../../types/public";
2
+ /**
3
+ *
4
+ * @category Devices
5
+ */
6
+ export declare function useMicrophone(): AudioDevice;
7
+ //# sourceMappingURL=useMicrophone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMicrophone.d.ts","sourceRoot":"","sources":["../../../src/hooks/devices/useMicrophone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD;;;GAGG;AACH,wBAAgB,aAAa,IAAI,WAAW,CAiC3C"}