@memberjunction/realtime-runtime 6.2.0-edge.3 → 6.2.0-edge.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/README.md +75 -0
- package/dist/channels/base-realtime-channel-client.d.ts +535 -28
- package/dist/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/channels/base-realtime-channel-client.js +864 -10
- package/dist/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/channels/channel-action-dispatcher.d.ts +70 -0
- package/dist/channels/channel-action-dispatcher.d.ts.map +1 -0
- package/dist/channels/channel-action-dispatcher.js +136 -0
- package/dist/channels/channel-action-dispatcher.js.map +1 -0
- package/dist/channels/channel-catalog-note.d.ts +48 -0
- package/dist/channels/channel-catalog-note.d.ts.map +1 -0
- package/dist/channels/channel-catalog-note.js +119 -0
- package/dist/channels/channel-catalog-note.js.map +1 -0
- package/dist/channels/channel-contract-types.d.ts +107 -0
- package/dist/channels/channel-contract-types.d.ts.map +1 -0
- package/dist/channels/channel-contract-types.js +12 -0
- package/dist/channels/channel-contract-types.js.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts +56 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.js +90 -0
- package/dist/channels/channel-descriptor-synthesis.js.map +1 -0
- package/dist/channels/channel-perception.d.ts +82 -0
- package/dist/channels/channel-perception.d.ts.map +1 -0
- package/dist/channels/channel-perception.js +120 -0
- package/dist/channels/channel-perception.js.map +1 -0
- package/dist/channels/channel-schema-format.d.ts +17 -0
- package/dist/channels/channel-schema-format.d.ts.map +1 -0
- package/dist/channels/channel-schema-format.js +45 -0
- package/dist/channels/channel-schema-format.js.map +1 -0
- package/dist/channels/channel-state-delta.d.ts +59 -0
- package/dist/channels/channel-state-delta.d.ts.map +1 -0
- package/dist/channels/channel-state-delta.js +116 -0
- package/dist/channels/channel-state-delta.js.map +1 -0
- package/dist/channels/channel-surface-placement.d.ts +25 -0
- package/dist/channels/channel-surface-placement.d.ts.map +1 -0
- package/dist/channels/channel-surface-placement.js +29 -0
- package/dist/channels/channel-surface-placement.js.map +1 -0
- package/dist/channels/channel-visual-pump.d.ts +126 -0
- package/dist/channels/channel-visual-pump.d.ts.map +1 -0
- package/dist/channels/channel-visual-pump.js +174 -0
- package/dist/channels/channel-visual-pump.js.map +1 -0
- package/dist/hosts/IRealtimeMediaHost.d.ts +86 -34
- package/dist/hosts/IRealtimeMediaHost.d.ts.map +1 -1
- package/dist/hosts/IRealtimeMediaHost.js +2 -1
- package/dist/hosts/IRealtimeMediaHost.js.map +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/session/RealtimeSessionRuntime.d.ts +984 -111
- package/dist/session/RealtimeSessionRuntime.d.ts.map +1 -1
- package/dist/session/RealtimeSessionRuntime.js +2396 -381
- package/dist/session/RealtimeSessionRuntime.js.map +1 -1
- package/dist/session/RecordingShardDrain.d.ts +59 -0
- package/dist/session/RecordingShardDrain.d.ts.map +1 -0
- package/dist/session/RecordingShardDrain.js +135 -0
- package/dist/session/RecordingShardDrain.js.map +1 -0
- package/dist/session/RecordingShardQueue.d.ts +94 -0
- package/dist/session/RecordingShardQueue.d.ts.map +1 -0
- package/dist/session/RecordingShardQueue.js +176 -0
- package/dist/session/RecordingShardQueue.js.map +1 -0
- package/dist/session/RecordingShardStore.d.ts +57 -0
- package/dist/session/RecordingShardStore.d.ts.map +1 -0
- package/dist/session/RecordingShardStore.js +184 -0
- package/dist/session/RecordingShardStore.js.map +1 -0
- package/dist/session/RecordingShardWindow.d.ts +42 -0
- package/dist/session/RecordingShardWindow.d.ts.map +1 -0
- package/dist/session/RecordingShardWindow.js +88 -0
- package/dist/session/RecordingShardWindow.js.map +1 -0
- package/dist/session/RecordingTransport.d.ts +66 -0
- package/dist/session/RecordingTransport.d.ts.map +1 -0
- package/dist/session/RecordingTransport.js +61 -0
- package/dist/session/RecordingTransport.js.map +1 -0
- package/dist/session/app-client-tool-registry.d.ts +76 -0
- package/dist/session/app-client-tool-registry.d.ts.map +1 -0
- package/dist/session/app-client-tool-registry.js +100 -0
- package/dist/session/app-client-tool-registry.js.map +1 -0
- package/dist/session/avatar-notice.d.ts +43 -0
- package/dist/session/avatar-notice.d.ts.map +1 -0
- package/dist/session/avatar-notice.js +60 -0
- package/dist/session/avatar-notice.js.map +1 -0
- package/dist/session/call-end.d.ts +21 -0
- package/dist/session/call-end.d.ts.map +1 -0
- package/dist/session/call-end.js +19 -0
- package/dist/session/call-end.js.map +1 -0
- package/dist/session/channel-exposure-preferences.d.ts +70 -0
- package/dist/session/channel-exposure-preferences.d.ts.map +1 -0
- package/dist/session/channel-exposure-preferences.js +138 -0
- package/dist/session/channel-exposure-preferences.js.map +1 -0
- package/dist/session/channel-session-scope.d.ts +142 -0
- package/dist/session/channel-session-scope.d.ts.map +1 -0
- package/dist/session/channel-session-scope.js +128 -0
- package/dist/session/channel-session-scope.js.map +1 -0
- package/dist/session/client-session-deadline.d.ts +28 -0
- package/dist/session/client-session-deadline.d.ts.map +1 -0
- package/dist/session/client-session-deadline.js +50 -0
- package/dist/session/client-session-deadline.js.map +1 -0
- package/dist/session/realtime-captures.d.ts +236 -0
- package/dist/session/realtime-captures.d.ts.map +1 -0
- package/dist/session/realtime-captures.js +383 -0
- package/dist/session/realtime-captures.js.map +1 -0
- package/dist/session/realtime-microphone.d.ts +50 -0
- package/dist/session/realtime-microphone.d.ts.map +1 -0
- package/dist/session/realtime-microphone.js +51 -0
- package/dist/session/realtime-microphone.js.map +1 -0
- package/dist/session/session-event-hub.d.ts +72 -0
- package/dist/session/session-event-hub.d.ts.map +1 -0
- package/dist/session/session-event-hub.js +151 -0
- package/dist/session/session-event-hub.js.map +1 -0
- package/dist/session/session-launcher.d.ts +128 -0
- package/dist/session/session-launcher.d.ts.map +1 -0
- package/dist/session/session-launcher.js +160 -0
- package/dist/session/session-launcher.js.map +1 -0
- package/package.json +10 -9
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The camera and the screen share of one realtime session: what the user chooses to show the agent
|
|
3
|
+
* besides the channels' own surfaces.
|
|
4
|
+
*
|
|
5
|
+
* A capture starts only when the user asks (a click in the host's UI) and in this order:
|
|
6
|
+
* 0. the session's policy must allow it ({@link RealtimeCapturesOptions.Admit}: the runtime admits a capture only while
|
|
7
|
+
* the channel that fronts it is in the session and lets the agent see pixels);
|
|
8
|
+
* 1. the model must be able to take it: an inbound video track is already live, or one is added now
|
|
9
|
+
* (`AddTrack`; a session never requests video before anyone shares, so it is not billed or limited for it);
|
|
10
|
+
* 2. the device or surface is opened: the camera through the host's `ILocalMediaController`, a screen, window or
|
|
11
|
+
* tab through the host's `RequestDisplayCapture`;
|
|
12
|
+
* 3. frames are sampled at the rate the track negotiated, by the host's sampler or the DOM default, and it becomes a
|
|
13
|
+
* source of the session's video source arbiter, which decides what the model sees and tells it when that changes.
|
|
14
|
+
* A sampler that cannot start fails the capture as `unsupported`: the capture does not report on, and the model is
|
|
15
|
+
* never told it can see it.
|
|
16
|
+
*
|
|
17
|
+
* With the camera check on ({@link RealtimeCapturesOptions.CameraCheck}), the first camera start waits between 2 and 3:
|
|
18
|
+
* the camera is open for the user to look at, and becomes a source only once the user confirms
|
|
19
|
+
* ({@link RealtimeCaptures.ConfirmCamera}).
|
|
20
|
+
*
|
|
21
|
+
* Stopping undoes all three. The track is removed again only when this class added it, no capture is left, and the
|
|
22
|
+
* arbiter has no other source: a video track the session was minted with (a channel's) stays, and so does one a channel
|
|
23
|
+
* started sending on while a capture was on. A camera that goes away (unplugged) or a share the user ends from the
|
|
24
|
+
* browser's own bar stops the capture the same way.
|
|
25
|
+
*
|
|
26
|
+
* @module @memberjunction/realtime-runtime
|
|
27
|
+
*/
|
|
28
|
+
import { type Observable } from 'rxjs';
|
|
29
|
+
import { type BaseRealtimeClient, type CapturedDisplaySurface, type DisplayCaptureOptions, type FrameSamplerOptions, type ILocalMediaController, type MediaDevice } from '@memberjunction/ai-realtime-client';
|
|
30
|
+
import type { IRealtimeFrameSampler, IRealtimeMediaHost } from '../hosts/IRealtimeMediaHost.js';
|
|
31
|
+
/** What the user can show the agent besides a channel's surface. */
|
|
32
|
+
export type RealtimeCaptureKind = 'camera' | 'screen';
|
|
33
|
+
/** Why a capture did not start. */
|
|
34
|
+
export type RealtimeCaptureFailure =
|
|
35
|
+
/** There is no live session to show it to. */
|
|
36
|
+
'no-session'
|
|
37
|
+
/** The model takes no (more) video, or the host cannot open this kind of capture. */
|
|
38
|
+
| 'unsupported'
|
|
39
|
+
/** The user, the operating system or the page's permissions policy refused. */
|
|
40
|
+
| 'denied'
|
|
41
|
+
/** There is no camera, or not the one asked for. */
|
|
42
|
+
| 'not-found'
|
|
43
|
+
/** The camera exists, but another application is using it. */
|
|
44
|
+
| 'in-use'
|
|
45
|
+
/** The user closed the browser's share picker without choosing. */
|
|
46
|
+
| 'cancelled'
|
|
47
|
+
/** The call's policy does not allow it: the channel that fronts it is not in the call, or may not show the agent video. */
|
|
48
|
+
| 'policy'
|
|
49
|
+
/** Anything else; see the message. */
|
|
50
|
+
| 'error';
|
|
51
|
+
/** One capture. */
|
|
52
|
+
export interface RealtimeCaptureState {
|
|
53
|
+
Status: 'off' | 'starting' | 'on' | 'failed';
|
|
54
|
+
/**
|
|
55
|
+
* The camera is open for the user to check, and the agent sees nothing until {@link RealtimeCaptures.ConfirmCamera}.
|
|
56
|
+
* Set only on a starting camera.
|
|
57
|
+
*/
|
|
58
|
+
Checking?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The live stream while on, or while the user checks the camera: the camera (show it mirrored) or the shared surface
|
|
61
|
+
* (show it as is).
|
|
62
|
+
*/
|
|
63
|
+
Stream?: MediaStream;
|
|
64
|
+
/** What a screen share shows, while on. */
|
|
65
|
+
Surface?: CapturedDisplaySurface;
|
|
66
|
+
/**
|
|
67
|
+
* The shared panel's name, while a share of one panel of the page is on and the host named it
|
|
68
|
+
* (`DisplayCaptureOptions.PanelLabel`). The agent sees the share as "<name> (shared panel)".
|
|
69
|
+
*/
|
|
70
|
+
PanelLabel?: string;
|
|
71
|
+
/** The camera in use, while the camera is open (being checked or on). */
|
|
72
|
+
DeviceID?: string;
|
|
73
|
+
/**
|
|
74
|
+
* The cameras the browser lists, while the camera is open: what {@link RealtimeCaptures.SwitchCamera} can move it to.
|
|
75
|
+
* A browser gives their names only once the user has allowed the camera.
|
|
76
|
+
*/
|
|
77
|
+
Devices?: MediaDevice[];
|
|
78
|
+
/** Why the last start failed, while failed. */
|
|
79
|
+
Failure?: RealtimeCaptureFailure;
|
|
80
|
+
/** What to tell the user about the failure. */
|
|
81
|
+
Message?: string;
|
|
82
|
+
}
|
|
83
|
+
/** Both captures. */
|
|
84
|
+
export interface RealtimeCaptureStates {
|
|
85
|
+
Camera: RealtimeCaptureState;
|
|
86
|
+
Screen: RealtimeCaptureState;
|
|
87
|
+
}
|
|
88
|
+
/** Which captures a call offers the user: those it could start now. */
|
|
89
|
+
export interface RealtimeCaptureOffers {
|
|
90
|
+
Camera: boolean;
|
|
91
|
+
Screen: boolean;
|
|
92
|
+
}
|
|
93
|
+
/** Neither capture offered: what a host shows outside a call. */
|
|
94
|
+
export declare const REALTIME_CAPTURE_OFFERS_NONE: RealtimeCaptureOffers;
|
|
95
|
+
/**
|
|
96
|
+
* Creates the sampler for one capture, as {@link IRealtimeMediaHost.CreateFrameSampler} does: the runtime passes the
|
|
97
|
+
* host's; the default draws through the DOM; a test passes one that needs none.
|
|
98
|
+
*/
|
|
99
|
+
export type RealtimeFrameSamplerFactory = (stream: MediaStream, options: FrameSamplerOptions) => IRealtimeFrameSampler;
|
|
100
|
+
/**
|
|
101
|
+
* The session's policy for one capture: refused with a reason for the user, or admitted with the channel it belongs to
|
|
102
|
+
* and whether the agent may see it yet.
|
|
103
|
+
*/
|
|
104
|
+
export type RealtimeCaptureAdmission = {
|
|
105
|
+
Admitted: false;
|
|
106
|
+
Message: string;
|
|
107
|
+
} | {
|
|
108
|
+
Admitted: true;
|
|
109
|
+
/** The channel that fronts the capture: the user's "agent can see" choice for it is kept under this key. */
|
|
110
|
+
ChannelKey?: string;
|
|
111
|
+
/** Whether the agent may see it now. When not, it runs for the user only until {@link RealtimeCaptures.SetVisibleToAgent}. */
|
|
112
|
+
VisibleToAgent: boolean;
|
|
113
|
+
};
|
|
114
|
+
export interface RealtimeCapturesOptions {
|
|
115
|
+
/** The session's realtime client: the arbiter's sink and the owner of the video track. */
|
|
116
|
+
Client: BaseRealtimeClient;
|
|
117
|
+
/** The host's camera-and-microphone controller; without one there is no camera. */
|
|
118
|
+
LocalMedia: ILocalMediaController | null;
|
|
119
|
+
/** The host, for screen sharing (`RequestDisplayCapture`); without it there is no screen share. */
|
|
120
|
+
Host: IRealtimeMediaHost;
|
|
121
|
+
/**
|
|
122
|
+
* Creates each capture's frame sampler: the host's ({@link IRealtimeMediaHost.CreateFrameSampler}), which the runtime
|
|
123
|
+
* passes here. Defaults to a DOM {@link FrameSampler}, which cannot start without a DOM.
|
|
124
|
+
*/
|
|
125
|
+
CreateSampler?: RealtimeFrameSamplerFactory;
|
|
126
|
+
/** Asked before each start, before anything is added or opened. Default: admitted, visible, with no channel. */
|
|
127
|
+
Admit?: (kind: RealtimeCaptureKind) => RealtimeCaptureAdmission;
|
|
128
|
+
/**
|
|
129
|
+
* Hold the first camera start for the user's check: the camera opens, and reaches the agent only after
|
|
130
|
+
* {@link RealtimeCaptures.ConfirmCamera}. Later starts skip the check. Default: off.
|
|
131
|
+
*/
|
|
132
|
+
CameraCheck?: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** Both captures off: what a host shows outside a session. */
|
|
135
|
+
export declare const REALTIME_CAPTURES_OFF: RealtimeCaptureStates;
|
|
136
|
+
/**
|
|
137
|
+
* The ids the camera and the screen share go by on the session's video source arbiter, and so on the runtime's
|
|
138
|
+
* `VideoSources$`; prefixed so they never meet a channel's (`Key#instance`). A host or channel finds a capture's source
|
|
139
|
+
* by its id there, to tell whether the model is being sent its frames (`Enabled` and `Active`).
|
|
140
|
+
*/
|
|
141
|
+
export declare const REALTIME_CAPTURE_SOURCE_IDS: Readonly<Record<RealtimeCaptureKind, string>>;
|
|
142
|
+
/** The camera and the screen share of one session. Created once the session is connected; disposed at its end. */
|
|
143
|
+
export declare class RealtimeCaptures {
|
|
144
|
+
private readonly options;
|
|
145
|
+
private readonly states;
|
|
146
|
+
private readonly live;
|
|
147
|
+
/** Bumped by every Stop, so a start that resolves after it knows it was cancelled. */
|
|
148
|
+
private readonly generation;
|
|
149
|
+
/** Whether this class added the session's inbound video track, and so may remove it. */
|
|
150
|
+
private addedVideoTrack;
|
|
151
|
+
/** Whether the user has confirmed the camera check; later camera starts skip it. */
|
|
152
|
+
private cameraChecked;
|
|
153
|
+
private disposed;
|
|
154
|
+
/** How each capture is shown to the agent: set at its start, kept up to date by {@link SetVisibleToAgent}. */
|
|
155
|
+
private readonly showing;
|
|
156
|
+
constructor(options: RealtimeCapturesOptions);
|
|
157
|
+
/** Both captures now. */
|
|
158
|
+
get States(): RealtimeCaptureStates;
|
|
159
|
+
/** Both captures, now and on every change. Completes on {@link Dispose}. */
|
|
160
|
+
get States$(): Observable<RealtimeCaptureStates>;
|
|
161
|
+
/**
|
|
162
|
+
* Starts a capture and shows it to the agent. Already starting or on, it returns where it is. Always resolves;
|
|
163
|
+
* a failure is a state, with a message for the user. A camera that waits for the user's check resolves still
|
|
164
|
+
* starting, with `Checking` set and the stream to preview.
|
|
165
|
+
*
|
|
166
|
+
* @param kind The camera or a screen share.
|
|
167
|
+
* @param options The camera's device id, or what the share picker offers first (and a panel to share).
|
|
168
|
+
*/
|
|
169
|
+
Start(kind: 'camera', options?: {
|
|
170
|
+
DeviceID?: string;
|
|
171
|
+
}): Promise<RealtimeCaptureState>;
|
|
172
|
+
Start(kind: 'screen', options?: DisplayCaptureOptions): Promise<RealtimeCaptureState>;
|
|
173
|
+
/**
|
|
174
|
+
* The user checked the camera and turned it on: it is shown to the agent from now on, and later camera starts skip the
|
|
175
|
+
* check. Returns the camera's state; unless the camera is waiting for its check, nothing changes. When frames cannot be
|
|
176
|
+
* taken from the camera, it fails as `unsupported` and the camera is released.
|
|
177
|
+
*/
|
|
178
|
+
ConfirmCamera(): RealtimeCaptureState;
|
|
179
|
+
/**
|
|
180
|
+
* Moves the open camera (being checked or on) to another of its {@link RealtimeCaptureState.Devices}. The stream stays
|
|
181
|
+
* the same, so the preview and the agent's frames carry on from the new camera. When the new camera cannot open, the
|
|
182
|
+
* host's controller goes back to the one in use; when that fails too, the camera stops. Resolves with the camera's
|
|
183
|
+
* state; nothing changes while the camera is not open.
|
|
184
|
+
*/
|
|
185
|
+
SwitchCamera(deviceId: string): Promise<RealtimeCaptureState>;
|
|
186
|
+
/**
|
|
187
|
+
* Lets the agent see a capture, or hides it from the agent while it keeps running for the user (the policy of the
|
|
188
|
+
* channel that fronts it changed). Applies to a capture that is still starting too. Quiet: the channel tells the model.
|
|
189
|
+
*/
|
|
190
|
+
SetVisibleToAgent(kind: RealtimeCaptureKind, visible: boolean): void;
|
|
191
|
+
/** Stops a capture: no more frames, out of the arbiter, the device or share released. Safe when it is off. */
|
|
192
|
+
Stop(kind: RealtimeCaptureKind): void;
|
|
193
|
+
/** Stops both captures and completes {@link States$}. */
|
|
194
|
+
Dispose(): void;
|
|
195
|
+
private stateOf;
|
|
196
|
+
private setState;
|
|
197
|
+
private fail;
|
|
198
|
+
/**
|
|
199
|
+
* Fails a capture whose device or share opened but whose frames cannot be taken for the agent (its sampler could not
|
|
200
|
+
* start): lets go of what opened, reports `unsupported`, and gives the track back.
|
|
201
|
+
*/
|
|
202
|
+
private failToShow;
|
|
203
|
+
/** The camera in use and the cameras to choose from, as the host's controller reports them now. */
|
|
204
|
+
private cameraDevices;
|
|
205
|
+
/**
|
|
206
|
+
* Keeps the open camera's device and list current as the controller reports them: a switch, a camera plugged in or
|
|
207
|
+
* removed, names that appear once the user allows the camera. While a switch is under way the device in use stays.
|
|
208
|
+
*/
|
|
209
|
+
private followCamera;
|
|
210
|
+
/**
|
|
211
|
+
* Makes sure an inbound video track is live: the one the session already has, or one added now.
|
|
212
|
+
*
|
|
213
|
+
* @returns Why the model cannot take video, or `null` when it can.
|
|
214
|
+
*/
|
|
215
|
+
private ensureVideoTrack;
|
|
216
|
+
/**
|
|
217
|
+
* Removes the video track this class added, once no capture is on or starting and the arbiter has no other source.
|
|
218
|
+
* A channel that started sending on the track while a capture was on (one opened mid-call before it had a track of its
|
|
219
|
+
* own) is such a source: removing the track would leave it sending nothing, right after the model was told it can see
|
|
220
|
+
* that channel. The track then stays for the session, as a channel's own does.
|
|
221
|
+
*/
|
|
222
|
+
private releaseVideoTrack;
|
|
223
|
+
/**
|
|
224
|
+
* Samples the capture at the negotiated rate and registers it with the arbiter under `label`, as its channel's and as
|
|
225
|
+
* visible as the policy allows. The sampler starts first: one that cannot start (no DOM, and no sampler from the host)
|
|
226
|
+
* leaves nothing registered, so the model is never told it can see a capture that would send it nothing.
|
|
227
|
+
*
|
|
228
|
+
* @returns The running sampler, or `null` when sampling could not start.
|
|
229
|
+
*/
|
|
230
|
+
private showToAgent;
|
|
231
|
+
/** Opens the camera through the host's controller, and stops the capture if the camera goes away. */
|
|
232
|
+
private openCamera;
|
|
233
|
+
/** Asks the host for a screen, window, tab or panel, and stops the capture when the share ends. */
|
|
234
|
+
private openScreen;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=realtime-captures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-captures.d.ts","sourceRoot":"","sources":["../../src/session/realtime-captures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The camera and the screen share of one realtime session: what the user chooses to show the agent
|
|
3
|
+
* besides the channels' own surfaces.
|
|
4
|
+
*
|
|
5
|
+
* A capture starts only when the user asks (a click in the host's UI) and in this order:
|
|
6
|
+
* 0. the session's policy must allow it ({@link RealtimeCapturesOptions.Admit}: the runtime admits a capture only while
|
|
7
|
+
* the channel that fronts it is in the session and lets the agent see pixels);
|
|
8
|
+
* 1. the model must be able to take it: an inbound video track is already live, or one is added now
|
|
9
|
+
* (`AddTrack`; a session never requests video before anyone shares, so it is not billed or limited for it);
|
|
10
|
+
* 2. the device or surface is opened: the camera through the host's `ILocalMediaController`, a screen, window or
|
|
11
|
+
* tab through the host's `RequestDisplayCapture`;
|
|
12
|
+
* 3. frames are sampled at the rate the track negotiated, by the host's sampler or the DOM default, and it becomes a
|
|
13
|
+
* source of the session's video source arbiter, which decides what the model sees and tells it when that changes.
|
|
14
|
+
* A sampler that cannot start fails the capture as `unsupported`: the capture does not report on, and the model is
|
|
15
|
+
* never told it can see it.
|
|
16
|
+
*
|
|
17
|
+
* With the camera check on ({@link RealtimeCapturesOptions.CameraCheck}), the first camera start waits between 2 and 3:
|
|
18
|
+
* the camera is open for the user to look at, and becomes a source only once the user confirms
|
|
19
|
+
* ({@link RealtimeCaptures.ConfirmCamera}).
|
|
20
|
+
*
|
|
21
|
+
* Stopping undoes all three. The track is removed again only when this class added it, no capture is left, and the
|
|
22
|
+
* arbiter has no other source: a video track the session was minted with (a channel's) stays, and so does one a channel
|
|
23
|
+
* started sending on while a capture was on. A camera that goes away (unplugged) or a share the user ends from the
|
|
24
|
+
* browser's own bar stops the capture the same way.
|
|
25
|
+
*
|
|
26
|
+
* @module @memberjunction/realtime-runtime
|
|
27
|
+
*/
|
|
28
|
+
import { BehaviorSubject } from 'rxjs';
|
|
29
|
+
import { FrameSampler, VideoSourceArbiter, } from '@memberjunction/ai-realtime-client';
|
|
30
|
+
/** Neither capture offered: what a host shows outside a call. */
|
|
31
|
+
export const REALTIME_CAPTURE_OFFERS_NONE = Object.freeze({ Camera: false, Screen: false });
|
|
32
|
+
const OFF = { Status: 'off' };
|
|
33
|
+
/** What a capture gets when the session sets no policy. */
|
|
34
|
+
const ADMITTED = { Admitted: true, VisibleToAgent: true };
|
|
35
|
+
/** Both captures off: what a host shows outside a session. */
|
|
36
|
+
export const REALTIME_CAPTURES_OFF = Object.freeze({ Camera: OFF, Screen: OFF });
|
|
37
|
+
/**
|
|
38
|
+
* The ids the camera and the screen share go by on the session's video source arbiter, and so on the runtime's
|
|
39
|
+
* `VideoSources$`; prefixed so they never meet a channel's (`Key#instance`). A host or channel finds a capture's source
|
|
40
|
+
* by its id there, to tell whether the model is being sent its frames (`Enabled` and `Active`).
|
|
41
|
+
*/
|
|
42
|
+
export const REALTIME_CAPTURE_SOURCE_IDS = Object.freeze({
|
|
43
|
+
camera: 'capture:camera',
|
|
44
|
+
screen: 'capture:screen',
|
|
45
|
+
});
|
|
46
|
+
/** The names the model is told it is looking at, and the "agent can see" list shows. A shared panel goes by its own. */
|
|
47
|
+
const LABELS = { camera: 'Camera', screen: 'Shared screen' };
|
|
48
|
+
/** What the user is told when frames cannot be taken from a capture for the agent: its sampler could not start. */
|
|
49
|
+
const CANNOT_SAMPLE = {
|
|
50
|
+
camera: 'This app cannot show the agent your camera.',
|
|
51
|
+
screen: 'This app cannot show the agent your screen.',
|
|
52
|
+
};
|
|
53
|
+
/** What the model and the "agent can see" list call a capture: a named shared panel by its name, as "Whiteboard (shared panel)". */
|
|
54
|
+
function sourceLabel(kind, panelLabel) {
|
|
55
|
+
return panelLabel ? `${panelLabel} (shared panel)` : LABELS[kind];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The inbound video track a capture needs. No rate: the model's own ceiling applies. The user's click is the
|
|
59
|
+
* consent the track asks for.
|
|
60
|
+
*/
|
|
61
|
+
const CAPTURE_VIDEO_TRACK = {
|
|
62
|
+
Modality: 'video',
|
|
63
|
+
Direction: 'inbound',
|
|
64
|
+
Encoding: 'image/jpeg',
|
|
65
|
+
UsageBasis: ['tokens', 'frames'],
|
|
66
|
+
RequiresConsent: true,
|
|
67
|
+
};
|
|
68
|
+
/** The camera and the screen share of one session. Created once the session is connected; disposed at its end. */
|
|
69
|
+
export class RealtimeCaptures {
|
|
70
|
+
constructor(options) {
|
|
71
|
+
this.options = options;
|
|
72
|
+
this.states = new BehaviorSubject(REALTIME_CAPTURES_OFF);
|
|
73
|
+
this.live = { camera: null, screen: null };
|
|
74
|
+
/** Bumped by every Stop, so a start that resolves after it knows it was cancelled. */
|
|
75
|
+
this.generation = { camera: 0, screen: 0 };
|
|
76
|
+
/** Whether this class added the session's inbound video track, and so may remove it. */
|
|
77
|
+
this.addedVideoTrack = false;
|
|
78
|
+
/** Whether the user has confirmed the camera check; later camera starts skip it. */
|
|
79
|
+
this.cameraChecked = false;
|
|
80
|
+
this.disposed = false;
|
|
81
|
+
/** How each capture is shown to the agent: set at its start, kept up to date by {@link SetVisibleToAgent}. */
|
|
82
|
+
this.showing = {
|
|
83
|
+
camera: { VisibleToAgent: true },
|
|
84
|
+
screen: { VisibleToAgent: true },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Both captures now. */
|
|
88
|
+
get States() {
|
|
89
|
+
return this.states.value;
|
|
90
|
+
}
|
|
91
|
+
/** Both captures, now and on every change. Completes on {@link Dispose}. */
|
|
92
|
+
get States$() {
|
|
93
|
+
return this.states.asObservable();
|
|
94
|
+
}
|
|
95
|
+
async Start(kind, options) {
|
|
96
|
+
const current = this.stateOf(kind);
|
|
97
|
+
if (this.disposed || current.Status === 'starting' || current.Status === 'on') {
|
|
98
|
+
return current;
|
|
99
|
+
}
|
|
100
|
+
const admission = this.options.Admit?.(kind) ?? ADMITTED;
|
|
101
|
+
if (!admission.Admitted) {
|
|
102
|
+
return this.fail(kind, 'policy', admission.Message);
|
|
103
|
+
}
|
|
104
|
+
this.showing[kind] = { ChannelKey: admission.ChannelKey, VisibleToAgent: admission.VisibleToAgent };
|
|
105
|
+
const generation = ++this.generation[kind];
|
|
106
|
+
this.setState(kind, { Status: 'starting' });
|
|
107
|
+
const refusal = this.ensureVideoTrack();
|
|
108
|
+
if (refusal) {
|
|
109
|
+
return this.fail(kind, 'unsupported', refusal);
|
|
110
|
+
}
|
|
111
|
+
const acquired = kind === 'camera' ? await this.openCamera(options) : await this.openScreen(options);
|
|
112
|
+
if (generation !== this.generation[kind]) {
|
|
113
|
+
// Stopped (or disposed) while opening: let go of what just opened.
|
|
114
|
+
if (acquired.Status === 'started') {
|
|
115
|
+
acquired.Release();
|
|
116
|
+
}
|
|
117
|
+
return this.stateOf(kind);
|
|
118
|
+
}
|
|
119
|
+
if (acquired.Status === 'failed') {
|
|
120
|
+
// Failed first, so this capture no longer counts as starting when the track is given back.
|
|
121
|
+
const failed = this.fail(kind, acquired.Failure, acquired.Message);
|
|
122
|
+
this.releaseVideoTrack();
|
|
123
|
+
return failed;
|
|
124
|
+
}
|
|
125
|
+
if (kind === 'camera' && this.options.CameraCheck && !this.cameraChecked) {
|
|
126
|
+
this.live.camera = { Stream: acquired.Stream, Sampler: null, Release: acquired.Release };
|
|
127
|
+
return this.setState('camera', { Status: 'starting', Checking: true, Stream: acquired.Stream, ...this.cameraDevices() });
|
|
128
|
+
}
|
|
129
|
+
const sampler = this.showToAgent(kind, acquired.Stream, sourceLabel(kind, acquired.PanelLabel));
|
|
130
|
+
if (!sampler) {
|
|
131
|
+
return this.failToShow(kind, acquired.Release);
|
|
132
|
+
}
|
|
133
|
+
this.live[kind] = { Stream: acquired.Stream, Sampler: sampler, Release: acquired.Release };
|
|
134
|
+
return this.setState(kind, {
|
|
135
|
+
Status: 'on',
|
|
136
|
+
Stream: acquired.Stream,
|
|
137
|
+
...(acquired.Surface ? { Surface: acquired.Surface } : {}),
|
|
138
|
+
...(acquired.PanelLabel ? { PanelLabel: acquired.PanelLabel } : {}),
|
|
139
|
+
...(kind === 'camera' ? this.cameraDevices() : {}),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The user checked the camera and turned it on: it is shown to the agent from now on, and later camera starts skip the
|
|
144
|
+
* check. Returns the camera's state; unless the camera is waiting for its check, nothing changes. When frames cannot be
|
|
145
|
+
* taken from the camera, it fails as `unsupported` and the camera is released.
|
|
146
|
+
*/
|
|
147
|
+
ConfirmCamera() {
|
|
148
|
+
const live = this.live.camera;
|
|
149
|
+
if (!live || !this.States.Camera.Checking) {
|
|
150
|
+
return this.States.Camera;
|
|
151
|
+
}
|
|
152
|
+
const sampler = this.showToAgent('camera', live.Stream, LABELS.camera);
|
|
153
|
+
if (!sampler) {
|
|
154
|
+
this.live.camera = null;
|
|
155
|
+
return this.failToShow('camera', live.Release);
|
|
156
|
+
}
|
|
157
|
+
this.cameraChecked = true;
|
|
158
|
+
live.Sampler = sampler;
|
|
159
|
+
return this.setState('camera', { Status: 'on', Stream: live.Stream, ...this.cameraDevices() });
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Moves the open camera (being checked or on) to another of its {@link RealtimeCaptureState.Devices}. The stream stays
|
|
163
|
+
* the same, so the preview and the agent's frames carry on from the new camera. When the new camera cannot open, the
|
|
164
|
+
* host's controller goes back to the one in use; when that fails too, the camera stops. Resolves with the camera's
|
|
165
|
+
* state; nothing changes while the camera is not open.
|
|
166
|
+
*/
|
|
167
|
+
async SwitchCamera(deviceId) {
|
|
168
|
+
const controller = this.options.LocalMedia;
|
|
169
|
+
if (this.live.camera && controller) {
|
|
170
|
+
await controller.SwitchDevice('camera', deviceId);
|
|
171
|
+
}
|
|
172
|
+
return this.States.Camera;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Lets the agent see a capture, or hides it from the agent while it keeps running for the user (the policy of the
|
|
176
|
+
* channel that fronts it changed). Applies to a capture that is still starting too. Quiet: the channel tells the model.
|
|
177
|
+
*/
|
|
178
|
+
SetVisibleToAgent(kind, visible) {
|
|
179
|
+
this.showing[kind] = { ...this.showing[kind], VisibleToAgent: visible };
|
|
180
|
+
if (this.live[kind]) {
|
|
181
|
+
VideoSourceArbiter.ForSink(this.options.Client).SetSourceEnabled(REALTIME_CAPTURE_SOURCE_IDS[kind], visible, false);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Stops a capture: no more frames, out of the arbiter, the device or share released. Safe when it is off. */
|
|
185
|
+
Stop(kind) {
|
|
186
|
+
this.generation[kind]++;
|
|
187
|
+
const live = this.live[kind];
|
|
188
|
+
this.live[kind] = null;
|
|
189
|
+
if (live) {
|
|
190
|
+
live.Sampler?.Stop();
|
|
191
|
+
VideoSourceArbiter.ForSink(this.options.Client).UnregisterSource(REALTIME_CAPTURE_SOURCE_IDS[kind]);
|
|
192
|
+
live.Release();
|
|
193
|
+
}
|
|
194
|
+
if (this.stateOf(kind).Status !== 'off') {
|
|
195
|
+
this.setState(kind, OFF);
|
|
196
|
+
}
|
|
197
|
+
this.releaseVideoTrack();
|
|
198
|
+
}
|
|
199
|
+
/** Stops both captures and completes {@link States$}. */
|
|
200
|
+
Dispose() {
|
|
201
|
+
if (this.disposed) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.Stop('camera');
|
|
205
|
+
this.Stop('screen');
|
|
206
|
+
this.disposed = true;
|
|
207
|
+
this.states.complete();
|
|
208
|
+
}
|
|
209
|
+
stateOf(kind) {
|
|
210
|
+
return kind === 'camera' ? this.states.value.Camera : this.states.value.Screen;
|
|
211
|
+
}
|
|
212
|
+
setState(kind, state) {
|
|
213
|
+
if (!this.disposed) {
|
|
214
|
+
this.states.next(kind === 'camera' ? { ...this.states.value, Camera: state } : { ...this.states.value, Screen: state });
|
|
215
|
+
}
|
|
216
|
+
return state;
|
|
217
|
+
}
|
|
218
|
+
fail(kind, failure, message) {
|
|
219
|
+
return this.setState(kind, { Status: 'failed', Failure: failure, Message: message });
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Fails a capture whose device or share opened but whose frames cannot be taken for the agent (its sampler could not
|
|
223
|
+
* start): lets go of what opened, reports `unsupported`, and gives the track back.
|
|
224
|
+
*/
|
|
225
|
+
failToShow(kind, release) {
|
|
226
|
+
release();
|
|
227
|
+
// Failed first, so this capture no longer counts as starting when the track is given back.
|
|
228
|
+
const failed = this.fail(kind, 'unsupported', CANNOT_SAMPLE[kind]);
|
|
229
|
+
this.releaseVideoTrack();
|
|
230
|
+
return failed;
|
|
231
|
+
}
|
|
232
|
+
/** The camera in use and the cameras to choose from, as the host's controller reports them now. */
|
|
233
|
+
cameraDevices() {
|
|
234
|
+
return cameraDevicesOf(this.options.LocalMedia?.State);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Keeps the open camera's device and list current as the controller reports them: a switch, a camera plugged in or
|
|
238
|
+
* removed, names that appear once the user allows the camera. While a switch is under way the device in use stays.
|
|
239
|
+
*/
|
|
240
|
+
followCamera(media) {
|
|
241
|
+
const current = this.States.Camera;
|
|
242
|
+
const next = cameraDevicesOf(media, current.DeviceID);
|
|
243
|
+
if (next.DeviceID !== current.DeviceID || !sameDevices(next.Devices, current.Devices ?? [])) {
|
|
244
|
+
this.setState('camera', { ...current, ...next });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Makes sure an inbound video track is live: the one the session already has, or one added now.
|
|
249
|
+
*
|
|
250
|
+
* @returns Why the model cannot take video, or `null` when it can.
|
|
251
|
+
*/
|
|
252
|
+
ensureVideoTrack() {
|
|
253
|
+
const client = this.options.Client;
|
|
254
|
+
if (client.IsTrackEstablished('video', 'inbound')) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const track = client.AddTrack(CAPTURE_VIDEO_TRACK);
|
|
258
|
+
if (!track || track.State !== 'live') {
|
|
259
|
+
return track?.Reason ?? 'This model cannot see video.';
|
|
260
|
+
}
|
|
261
|
+
this.addedVideoTrack = true;
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Removes the video track this class added, once no capture is on or starting and the arbiter has no other source.
|
|
266
|
+
* A channel that started sending on the track while a capture was on (one opened mid-call before it had a track of its
|
|
267
|
+
* own) is such a source: removing the track would leave it sending nothing, right after the model was told it can see
|
|
268
|
+
* that channel. The track then stays for the session, as a channel's own does.
|
|
269
|
+
*/
|
|
270
|
+
releaseVideoTrack() {
|
|
271
|
+
const busy = [this.states.value.Camera, this.states.value.Screen].some((s) => s.Status === 'on' || s.Status === 'starting');
|
|
272
|
+
if (!this.addedVideoTrack || busy || VideoSourceArbiter.ForSink(this.options.Client).GetSources().length > 0) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.addedVideoTrack = false;
|
|
276
|
+
this.options.Client.RemoveTrack(CAPTURE_VIDEO_TRACK);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Samples the capture at the negotiated rate and registers it with the arbiter under `label`, as its channel's and as
|
|
280
|
+
* visible as the policy allows. The sampler starts first: one that cannot start (no DOM, and no sampler from the host)
|
|
281
|
+
* leaves nothing registered, so the model is never told it can see a capture that would send it nothing.
|
|
282
|
+
*
|
|
283
|
+
* @returns The running sampler, or `null` when sampling could not start.
|
|
284
|
+
*/
|
|
285
|
+
showToAgent(kind, stream, label) {
|
|
286
|
+
const arbiter = VideoSourceArbiter.ForSink(this.options.Client);
|
|
287
|
+
const sourceId = REALTIME_CAPTURE_SOURCE_IDS[kind];
|
|
288
|
+
const create = this.options.CreateSampler ?? createDomSampler;
|
|
289
|
+
const sampler = create(stream, {
|
|
290
|
+
Rate: this.options.Client.InboundVideoRate ?? 1,
|
|
291
|
+
OnFrame: (frame) => arbiter.PushFrame(sourceId, frame.Data, frame.MimeType),
|
|
292
|
+
});
|
|
293
|
+
if (!sampler.Start()) {
|
|
294
|
+
sampler.Stop();
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const { ChannelKey, VisibleToAgent } = this.showing[kind];
|
|
298
|
+
arbiter.RegisterSource({ SourceID: sourceId, Label: label, Kind: kind, Enabled: VisibleToAgent, ...(ChannelKey ? { ChannelKey } : {}) });
|
|
299
|
+
return sampler;
|
|
300
|
+
}
|
|
301
|
+
/** Opens the camera through the host's controller, and stops the capture if the camera goes away. */
|
|
302
|
+
async openCamera(options) {
|
|
303
|
+
const controller = this.options.LocalMedia;
|
|
304
|
+
if (!controller) {
|
|
305
|
+
return { Status: 'failed', Failure: 'unsupported', Message: 'This app cannot open a camera.' };
|
|
306
|
+
}
|
|
307
|
+
const result = await controller.Start('camera', options?.DeviceID);
|
|
308
|
+
if (result.Status === 'failed') {
|
|
309
|
+
return { Status: 'failed', Failure: result.Reason, Message: result.Message };
|
|
310
|
+
}
|
|
311
|
+
const watch = controller.State$.subscribe((state) => {
|
|
312
|
+
if (state.Camera.Status === 'off' || state.Camera.Status === 'failed') {
|
|
313
|
+
this.Stop('camera');
|
|
314
|
+
}
|
|
315
|
+
else if (this.live.camera) {
|
|
316
|
+
this.followCamera(state);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
return {
|
|
320
|
+
Status: 'started',
|
|
321
|
+
Stream: result.Stream,
|
|
322
|
+
Release: () => {
|
|
323
|
+
watch.unsubscribe();
|
|
324
|
+
controller.Stop('camera');
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/** Asks the host for a screen, window, tab or panel, and stops the capture when the share ends. */
|
|
329
|
+
async openScreen(options) {
|
|
330
|
+
const host = this.options.Host;
|
|
331
|
+
if (!host.RequestDisplayCapture) {
|
|
332
|
+
return { Status: 'failed', Failure: 'unsupported', Message: 'This app cannot share a screen.' };
|
|
333
|
+
}
|
|
334
|
+
const result = await host.RequestDisplayCapture(options);
|
|
335
|
+
if (result.Status === 'cancelled') {
|
|
336
|
+
return { Status: 'failed', Failure: 'cancelled', Message: 'Sharing was cancelled.' };
|
|
337
|
+
}
|
|
338
|
+
if (result.Status === 'failed') {
|
|
339
|
+
return { Status: 'failed', Failure: screenFailure(result.Reason), Message: result.Message };
|
|
340
|
+
}
|
|
341
|
+
const capture = result.Capture;
|
|
342
|
+
const stopWatching = capture.OnEnded(() => this.Stop('screen'));
|
|
343
|
+
return {
|
|
344
|
+
Status: 'started',
|
|
345
|
+
Stream: capture.Stream,
|
|
346
|
+
Surface: capture.Surface,
|
|
347
|
+
...(capture.PanelLabel ? { PanelLabel: capture.PanelLabel } : {}),
|
|
348
|
+
Release: () => {
|
|
349
|
+
stopWatching();
|
|
350
|
+
capture.Stop();
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* The camera in use and the cameras among the devices, as a controller reports them. The camera in use is known while
|
|
357
|
+
* the controller reports it on; otherwise it is `inUse`, the one the capture already names.
|
|
358
|
+
*/
|
|
359
|
+
function cameraDevicesOf(media, inUse) {
|
|
360
|
+
const deviceId = (media?.Camera.Status === 'on' ? media.Camera.DeviceID : undefined) ?? inUse;
|
|
361
|
+
return { ...(deviceId ? { DeviceID: deviceId } : {}), Devices: media?.Devices.filter((d) => d.Kind === 'camera') ?? [] };
|
|
362
|
+
}
|
|
363
|
+
/** Whether two lists name the same devices in the same order. */
|
|
364
|
+
function sameDevices(a, b) {
|
|
365
|
+
return a.length === b.length && a.every((d, i) => d.DeviceID === b[i].DeviceID && d.Label === b[i].Label);
|
|
366
|
+
}
|
|
367
|
+
/** The default sampler: a DOM {@link FrameSampler}, whose `Start()` returns `false` without a DOM. */
|
|
368
|
+
function createDomSampler(stream, options) {
|
|
369
|
+
return new FrameSampler(stream, options);
|
|
370
|
+
}
|
|
371
|
+
/** A share failure as a capture failure: a panel the browser cannot narrow to is unsupported; a wrong one an error. */
|
|
372
|
+
function screenFailure(reason) {
|
|
373
|
+
switch (reason) {
|
|
374
|
+
case 'unsupported':
|
|
375
|
+
case 'panel-unsupported':
|
|
376
|
+
return 'unsupported';
|
|
377
|
+
case 'denied':
|
|
378
|
+
return 'denied';
|
|
379
|
+
default:
|
|
380
|
+
return 'error';
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=realtime-captures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-captures.js","sourceRoot":"","sources":["../../src/session/realtime-captures.ts"],"names":[],"mappings":"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"}
|