@furious.luke/argus-js 0.1.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.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/dist/index.cjs +337 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +224 -0
- package/dist/index.d.ts +224 -0
- package/dist/index.js +308 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message types exchanged over the WebSocket signaling channel.
|
|
3
|
+
*/
|
|
4
|
+
type SignalMessage = {
|
|
5
|
+
type: "offer";
|
|
6
|
+
sdp: string;
|
|
7
|
+
sdp_type: "offer";
|
|
8
|
+
} | {
|
|
9
|
+
type: "answer";
|
|
10
|
+
sdp: string;
|
|
11
|
+
sdp_type: "answer";
|
|
12
|
+
} | {
|
|
13
|
+
type: "ice_candidate";
|
|
14
|
+
candidate: string;
|
|
15
|
+
sdp_mid?: string;
|
|
16
|
+
sdp_mline_index?: number;
|
|
17
|
+
username_fragment?: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: "connection_state";
|
|
20
|
+
state: string;
|
|
21
|
+
} | {
|
|
22
|
+
type: "error";
|
|
23
|
+
error: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: "accepted";
|
|
26
|
+
} | {
|
|
27
|
+
type: "proceed";
|
|
28
|
+
} | {
|
|
29
|
+
type: "ready";
|
|
30
|
+
turn_url?: string;
|
|
31
|
+
turn_username?: string;
|
|
32
|
+
turn_credential?: string;
|
|
33
|
+
read_token?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Callbacks emitted by the Publisher during its lifecycle.
|
|
37
|
+
*/
|
|
38
|
+
interface PublisherCallbacks {
|
|
39
|
+
/** Called when the WebRTC peer connection state changes. */
|
|
40
|
+
onConnectionStateChange?: (state: RTCPeerConnectionState) => void;
|
|
41
|
+
/** Called when a fatal error occurs (e.g. signaling error, WebSocket close). */
|
|
42
|
+
onError?: (error: Error) => void;
|
|
43
|
+
/** Called when the browser has successfully connected to the media server. */
|
|
44
|
+
onConnected?: () => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* TURN and read-token info delivered in the gateway `ready` message.
|
|
48
|
+
*/
|
|
49
|
+
interface GatewayReadyInfo {
|
|
50
|
+
turn_url?: string;
|
|
51
|
+
turn_username?: string;
|
|
52
|
+
turn_credential?: string;
|
|
53
|
+
read_token?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Options for publishing a media stream.
|
|
57
|
+
*/
|
|
58
|
+
interface PublisherOptions {
|
|
59
|
+
/** Gateway WebSocket URLs returned by POST /api/streams `gateway_urls`. All are raced simultaneously. */
|
|
60
|
+
gatewayURLs: string[];
|
|
61
|
+
/** The short-lived join token from POST /api/streams. */
|
|
62
|
+
token: string;
|
|
63
|
+
/** Optional extra ICE servers (e.g. STUN). TURN is supplied by the winning gateway. */
|
|
64
|
+
iceServers?: RTCIceServer[];
|
|
65
|
+
/** Callbacks for lifecycle events. */
|
|
66
|
+
callbacks?: PublisherCallbacks;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Publisher streams a browser {@link MediaStream} to an Argus media server over
|
|
71
|
+
* WebRTC. Given the `gateway_urls` and `token` from a join-token response, it
|
|
72
|
+
* races the candidate gateways to the fastest one, completes the two-phase
|
|
73
|
+
* signaling handshake, and manages the peer connection — offer/answer exchange,
|
|
74
|
+
* ICE candidate trickling, and track (re)negotiation.
|
|
75
|
+
*
|
|
76
|
+
* After {@link Publisher.start} resolves, {@link Publisher.frameReadToken} holds
|
|
77
|
+
* the token your application server needs to fetch frames for this stream.
|
|
78
|
+
*
|
|
79
|
+
* @example Publish the default camera:
|
|
80
|
+
* ```ts
|
|
81
|
+
* const pub = new Publisher({
|
|
82
|
+
* gatewayURLs: joinResp.gateway_urls,
|
|
83
|
+
* token: joinResp.token,
|
|
84
|
+
* callbacks: { onConnected: () => console.log("live!") },
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
88
|
+
* await pub.start(stream);
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare class Publisher {
|
|
92
|
+
private opts;
|
|
93
|
+
private sig;
|
|
94
|
+
private pc;
|
|
95
|
+
private hasAnswer;
|
|
96
|
+
private pendingRemoteCandidates;
|
|
97
|
+
private localStream;
|
|
98
|
+
private readToken;
|
|
99
|
+
constructor(opts: PublisherOptions);
|
|
100
|
+
/** The read token received from the gateway after connecting, for fetching frames. */
|
|
101
|
+
get frameReadToken(): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Starts the publisher: races all gateways to find the fastest, completes
|
|
104
|
+
* the two-phase handshake, creates the peer connection, and sends the SDP
|
|
105
|
+
* offer. Resolves when the offer has been sent (not when ICE completes —
|
|
106
|
+
* use onConnected for that).
|
|
107
|
+
*/
|
|
108
|
+
start(stream: MediaStream): Promise<void>;
|
|
109
|
+
/** Replaces the currently published stream with a new one. */
|
|
110
|
+
replaceStream(stream: MediaStream): Promise<void>;
|
|
111
|
+
/** Stops publishing and tears down the peer connection. */
|
|
112
|
+
stop(): void;
|
|
113
|
+
/** Returns the current RTCPeerConnection, or null if not started. */
|
|
114
|
+
get peerConnection(): RTCPeerConnection | null;
|
|
115
|
+
/** Returns true if the peer connection is in the "connected" state. */
|
|
116
|
+
get isConnected(): boolean;
|
|
117
|
+
private raceGateways;
|
|
118
|
+
private handleSignal;
|
|
119
|
+
/** Waits for ICE gathering to reach the "complete" state. */
|
|
120
|
+
private gatherComplete;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Options for {@link captureCamera}.
|
|
125
|
+
*
|
|
126
|
+
* These are merged shallowly over the library defaults: any field you provide
|
|
127
|
+
* replaces the default for that field entirely (e.g. passing `video` overrides
|
|
128
|
+
* the default video constraints rather than merging into them). Omit a field to
|
|
129
|
+
* keep its default.
|
|
130
|
+
*/
|
|
131
|
+
interface CaptureCameraOptions {
|
|
132
|
+
/**
|
|
133
|
+
* Video constraints, or `true`/`false`. Defaults to a modest resolution and
|
|
134
|
+
* frame rate (see {@link captureCamera}). Set `false` to disable video.
|
|
135
|
+
*/
|
|
136
|
+
video?: MediaTrackConstraints | boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Audio constraints, or `true`/`false`. Defaults to `false` — Argus is a
|
|
139
|
+
* video-frame streaming system, so audio is off unless you ask for it.
|
|
140
|
+
*/
|
|
141
|
+
audio?: MediaTrackConstraints | boolean;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Options for {@link captureScreen}.
|
|
145
|
+
*
|
|
146
|
+
* These are merged shallowly over the library defaults: any field you provide
|
|
147
|
+
* replaces the default for that field entirely. Omit a field to keep its
|
|
148
|
+
* default.
|
|
149
|
+
*/
|
|
150
|
+
interface CaptureScreenOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Video constraints, or `true`. Defaults to a capped width and a low frame
|
|
153
|
+
* rate (see {@link captureScreen}).
|
|
154
|
+
*/
|
|
155
|
+
video?: MediaTrackConstraints | boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Audio constraints, or `true`/`false`. Defaults to `false` — screen shares
|
|
158
|
+
* rarely need audio for a video-frame streaming system.
|
|
159
|
+
*/
|
|
160
|
+
audio?: MediaTrackConstraints | boolean;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Captures the user's camera via `navigator.mediaDevices.getUserMedia`,
|
|
164
|
+
* applying sensible defaults for a video-frame streaming system.
|
|
165
|
+
*
|
|
166
|
+
* Defaults:
|
|
167
|
+
* - `video`: `{ width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 30 } }`
|
|
168
|
+
* — a modest resolution that keeps upload bandwidth reasonable. Cameras are
|
|
169
|
+
* rarely the 4k bandwidth problem that screen capture is, so this is an
|
|
170
|
+
* `ideal` (a hint) rather than a hard cap.
|
|
171
|
+
* - `audio`: `false` — this is a video-frame streaming system.
|
|
172
|
+
*
|
|
173
|
+
* Any option you pass replaces the corresponding default outright (shallow
|
|
174
|
+
* merge), so pass a full `video` constraints object if you want to tweak it.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* const stream = await captureCamera();
|
|
179
|
+
* await publisher.start(stream);
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @example Front camera with audio:
|
|
183
|
+
* ```ts
|
|
184
|
+
* const stream = await captureCamera({
|
|
185
|
+
* video: { facingMode: "user" },
|
|
186
|
+
* audio: true,
|
|
187
|
+
* });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
declare function captureCamera(opts?: CaptureCameraOptions): Promise<MediaStream>;
|
|
191
|
+
/**
|
|
192
|
+
* Captures a screen / window / tab via
|
|
193
|
+
* `navigator.mediaDevices.getDisplayMedia`, applying sensible defaults tuned to
|
|
194
|
+
* avoid the HiDPI/Retina bandwidth trap.
|
|
195
|
+
*
|
|
196
|
+
* Defaults:
|
|
197
|
+
* - `video`: `{ width: { max: 1920 }, frameRate: { ideal: 5, max: 10 } }`.
|
|
198
|
+
* The `width` is a **`max`, not an `ideal`**: on a 2x-Retina display the
|
|
199
|
+
* browser would otherwise capture at native resolution (often 3456px+ /
|
|
200
|
+
* effectively 4k), wasting upload bandwidth and downstream decode cost for no
|
|
201
|
+
* visible benefit. Capping the max roughly halves a 2x-Retina share while
|
|
202
|
+
* leaving smaller displays untouched. Screen content is mostly static, so the
|
|
203
|
+
* low frame rate saves further bandwidth.
|
|
204
|
+
* - `audio`: `false`.
|
|
205
|
+
*
|
|
206
|
+
* IMPORTANT — do NOT add `resizeMode: "none"` here. That value forbids the
|
|
207
|
+
* browser from downscaling the source, which turns the `width: { max: 1920 }`
|
|
208
|
+
* cap into a no-op on exactly the Retina displays it targets. By omitting
|
|
209
|
+
* `resizeMode` we let the user agent scale to satisfy the constraint (its
|
|
210
|
+
* default behaviour), which is the entire point of this helper. It is tempting
|
|
211
|
+
* to add `resizeMode: "none"` back for "sharpness" — don't.
|
|
212
|
+
*
|
|
213
|
+
* Any option you pass replaces the corresponding default outright (shallow
|
|
214
|
+
* merge), so pass a full `video` constraints object if you want to tweak it.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* const stream = await captureScreen();
|
|
219
|
+
* await publisher.start(stream);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare function captureScreen(opts?: CaptureScreenOptions): Promise<MediaStream>;
|
|
223
|
+
|
|
224
|
+
export { type CaptureCameraOptions, type CaptureScreenOptions, type GatewayReadyInfo, Publisher, type PublisherCallbacks, type PublisherOptions, type SignalMessage, captureCamera, captureScreen };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message types exchanged over the WebSocket signaling channel.
|
|
3
|
+
*/
|
|
4
|
+
type SignalMessage = {
|
|
5
|
+
type: "offer";
|
|
6
|
+
sdp: string;
|
|
7
|
+
sdp_type: "offer";
|
|
8
|
+
} | {
|
|
9
|
+
type: "answer";
|
|
10
|
+
sdp: string;
|
|
11
|
+
sdp_type: "answer";
|
|
12
|
+
} | {
|
|
13
|
+
type: "ice_candidate";
|
|
14
|
+
candidate: string;
|
|
15
|
+
sdp_mid?: string;
|
|
16
|
+
sdp_mline_index?: number;
|
|
17
|
+
username_fragment?: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: "connection_state";
|
|
20
|
+
state: string;
|
|
21
|
+
} | {
|
|
22
|
+
type: "error";
|
|
23
|
+
error: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: "accepted";
|
|
26
|
+
} | {
|
|
27
|
+
type: "proceed";
|
|
28
|
+
} | {
|
|
29
|
+
type: "ready";
|
|
30
|
+
turn_url?: string;
|
|
31
|
+
turn_username?: string;
|
|
32
|
+
turn_credential?: string;
|
|
33
|
+
read_token?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Callbacks emitted by the Publisher during its lifecycle.
|
|
37
|
+
*/
|
|
38
|
+
interface PublisherCallbacks {
|
|
39
|
+
/** Called when the WebRTC peer connection state changes. */
|
|
40
|
+
onConnectionStateChange?: (state: RTCPeerConnectionState) => void;
|
|
41
|
+
/** Called when a fatal error occurs (e.g. signaling error, WebSocket close). */
|
|
42
|
+
onError?: (error: Error) => void;
|
|
43
|
+
/** Called when the browser has successfully connected to the media server. */
|
|
44
|
+
onConnected?: () => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* TURN and read-token info delivered in the gateway `ready` message.
|
|
48
|
+
*/
|
|
49
|
+
interface GatewayReadyInfo {
|
|
50
|
+
turn_url?: string;
|
|
51
|
+
turn_username?: string;
|
|
52
|
+
turn_credential?: string;
|
|
53
|
+
read_token?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Options for publishing a media stream.
|
|
57
|
+
*/
|
|
58
|
+
interface PublisherOptions {
|
|
59
|
+
/** Gateway WebSocket URLs returned by POST /api/streams `gateway_urls`. All are raced simultaneously. */
|
|
60
|
+
gatewayURLs: string[];
|
|
61
|
+
/** The short-lived join token from POST /api/streams. */
|
|
62
|
+
token: string;
|
|
63
|
+
/** Optional extra ICE servers (e.g. STUN). TURN is supplied by the winning gateway. */
|
|
64
|
+
iceServers?: RTCIceServer[];
|
|
65
|
+
/** Callbacks for lifecycle events. */
|
|
66
|
+
callbacks?: PublisherCallbacks;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Publisher streams a browser {@link MediaStream} to an Argus media server over
|
|
71
|
+
* WebRTC. Given the `gateway_urls` and `token` from a join-token response, it
|
|
72
|
+
* races the candidate gateways to the fastest one, completes the two-phase
|
|
73
|
+
* signaling handshake, and manages the peer connection — offer/answer exchange,
|
|
74
|
+
* ICE candidate trickling, and track (re)negotiation.
|
|
75
|
+
*
|
|
76
|
+
* After {@link Publisher.start} resolves, {@link Publisher.frameReadToken} holds
|
|
77
|
+
* the token your application server needs to fetch frames for this stream.
|
|
78
|
+
*
|
|
79
|
+
* @example Publish the default camera:
|
|
80
|
+
* ```ts
|
|
81
|
+
* const pub = new Publisher({
|
|
82
|
+
* gatewayURLs: joinResp.gateway_urls,
|
|
83
|
+
* token: joinResp.token,
|
|
84
|
+
* callbacks: { onConnected: () => console.log("live!") },
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
88
|
+
* await pub.start(stream);
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare class Publisher {
|
|
92
|
+
private opts;
|
|
93
|
+
private sig;
|
|
94
|
+
private pc;
|
|
95
|
+
private hasAnswer;
|
|
96
|
+
private pendingRemoteCandidates;
|
|
97
|
+
private localStream;
|
|
98
|
+
private readToken;
|
|
99
|
+
constructor(opts: PublisherOptions);
|
|
100
|
+
/** The read token received from the gateway after connecting, for fetching frames. */
|
|
101
|
+
get frameReadToken(): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Starts the publisher: races all gateways to find the fastest, completes
|
|
104
|
+
* the two-phase handshake, creates the peer connection, and sends the SDP
|
|
105
|
+
* offer. Resolves when the offer has been sent (not when ICE completes —
|
|
106
|
+
* use onConnected for that).
|
|
107
|
+
*/
|
|
108
|
+
start(stream: MediaStream): Promise<void>;
|
|
109
|
+
/** Replaces the currently published stream with a new one. */
|
|
110
|
+
replaceStream(stream: MediaStream): Promise<void>;
|
|
111
|
+
/** Stops publishing and tears down the peer connection. */
|
|
112
|
+
stop(): void;
|
|
113
|
+
/** Returns the current RTCPeerConnection, or null if not started. */
|
|
114
|
+
get peerConnection(): RTCPeerConnection | null;
|
|
115
|
+
/** Returns true if the peer connection is in the "connected" state. */
|
|
116
|
+
get isConnected(): boolean;
|
|
117
|
+
private raceGateways;
|
|
118
|
+
private handleSignal;
|
|
119
|
+
/** Waits for ICE gathering to reach the "complete" state. */
|
|
120
|
+
private gatherComplete;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Options for {@link captureCamera}.
|
|
125
|
+
*
|
|
126
|
+
* These are merged shallowly over the library defaults: any field you provide
|
|
127
|
+
* replaces the default for that field entirely (e.g. passing `video` overrides
|
|
128
|
+
* the default video constraints rather than merging into them). Omit a field to
|
|
129
|
+
* keep its default.
|
|
130
|
+
*/
|
|
131
|
+
interface CaptureCameraOptions {
|
|
132
|
+
/**
|
|
133
|
+
* Video constraints, or `true`/`false`. Defaults to a modest resolution and
|
|
134
|
+
* frame rate (see {@link captureCamera}). Set `false` to disable video.
|
|
135
|
+
*/
|
|
136
|
+
video?: MediaTrackConstraints | boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Audio constraints, or `true`/`false`. Defaults to `false` — Argus is a
|
|
139
|
+
* video-frame streaming system, so audio is off unless you ask for it.
|
|
140
|
+
*/
|
|
141
|
+
audio?: MediaTrackConstraints | boolean;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Options for {@link captureScreen}.
|
|
145
|
+
*
|
|
146
|
+
* These are merged shallowly over the library defaults: any field you provide
|
|
147
|
+
* replaces the default for that field entirely. Omit a field to keep its
|
|
148
|
+
* default.
|
|
149
|
+
*/
|
|
150
|
+
interface CaptureScreenOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Video constraints, or `true`. Defaults to a capped width and a low frame
|
|
153
|
+
* rate (see {@link captureScreen}).
|
|
154
|
+
*/
|
|
155
|
+
video?: MediaTrackConstraints | boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Audio constraints, or `true`/`false`. Defaults to `false` — screen shares
|
|
158
|
+
* rarely need audio for a video-frame streaming system.
|
|
159
|
+
*/
|
|
160
|
+
audio?: MediaTrackConstraints | boolean;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Captures the user's camera via `navigator.mediaDevices.getUserMedia`,
|
|
164
|
+
* applying sensible defaults for a video-frame streaming system.
|
|
165
|
+
*
|
|
166
|
+
* Defaults:
|
|
167
|
+
* - `video`: `{ width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 30 } }`
|
|
168
|
+
* — a modest resolution that keeps upload bandwidth reasonable. Cameras are
|
|
169
|
+
* rarely the 4k bandwidth problem that screen capture is, so this is an
|
|
170
|
+
* `ideal` (a hint) rather than a hard cap.
|
|
171
|
+
* - `audio`: `false` — this is a video-frame streaming system.
|
|
172
|
+
*
|
|
173
|
+
* Any option you pass replaces the corresponding default outright (shallow
|
|
174
|
+
* merge), so pass a full `video` constraints object if you want to tweak it.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* const stream = await captureCamera();
|
|
179
|
+
* await publisher.start(stream);
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @example Front camera with audio:
|
|
183
|
+
* ```ts
|
|
184
|
+
* const stream = await captureCamera({
|
|
185
|
+
* video: { facingMode: "user" },
|
|
186
|
+
* audio: true,
|
|
187
|
+
* });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
declare function captureCamera(opts?: CaptureCameraOptions): Promise<MediaStream>;
|
|
191
|
+
/**
|
|
192
|
+
* Captures a screen / window / tab via
|
|
193
|
+
* `navigator.mediaDevices.getDisplayMedia`, applying sensible defaults tuned to
|
|
194
|
+
* avoid the HiDPI/Retina bandwidth trap.
|
|
195
|
+
*
|
|
196
|
+
* Defaults:
|
|
197
|
+
* - `video`: `{ width: { max: 1920 }, frameRate: { ideal: 5, max: 10 } }`.
|
|
198
|
+
* The `width` is a **`max`, not an `ideal`**: on a 2x-Retina display the
|
|
199
|
+
* browser would otherwise capture at native resolution (often 3456px+ /
|
|
200
|
+
* effectively 4k), wasting upload bandwidth and downstream decode cost for no
|
|
201
|
+
* visible benefit. Capping the max roughly halves a 2x-Retina share while
|
|
202
|
+
* leaving smaller displays untouched. Screen content is mostly static, so the
|
|
203
|
+
* low frame rate saves further bandwidth.
|
|
204
|
+
* - `audio`: `false`.
|
|
205
|
+
*
|
|
206
|
+
* IMPORTANT — do NOT add `resizeMode: "none"` here. That value forbids the
|
|
207
|
+
* browser from downscaling the source, which turns the `width: { max: 1920 }`
|
|
208
|
+
* cap into a no-op on exactly the Retina displays it targets. By omitting
|
|
209
|
+
* `resizeMode` we let the user agent scale to satisfy the constraint (its
|
|
210
|
+
* default behaviour), which is the entire point of this helper. It is tempting
|
|
211
|
+
* to add `resizeMode: "none"` back for "sharpness" — don't.
|
|
212
|
+
*
|
|
213
|
+
* Any option you pass replaces the corresponding default outright (shallow
|
|
214
|
+
* merge), so pass a full `video` constraints object if you want to tweak it.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* const stream = await captureScreen();
|
|
219
|
+
* await publisher.start(stream);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare function captureScreen(opts?: CaptureScreenOptions): Promise<MediaStream>;
|
|
223
|
+
|
|
224
|
+
export { type CaptureCameraOptions, type CaptureScreenOptions, type GatewayReadyInfo, Publisher, type PublisherCallbacks, type PublisherOptions, type SignalMessage, captureCamera, captureScreen };
|