@mediaway/device 0.1.5 → 0.1.6
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 +35 -6
- package/dist/index.d.ts +94 -15
- package/dist/index.js +169 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,24 +55,53 @@ await mic.close();
|
|
|
55
55
|
|
|
56
56
|
## Screen capture
|
|
57
57
|
|
|
58
|
+
GPU-only, Zero-Copy — there is no CPU fallback in the wrapped Rust backend
|
|
59
|
+
(`adr/0003-gpu-handle-c-abi.md` §4), so `openScreenCapture()` needs a live GPU
|
|
60
|
+
device. It creates one internally by default (via the GPU device factory,
|
|
61
|
+
`mediaway-device` ADR-0007):
|
|
62
|
+
|
|
58
63
|
```ts
|
|
59
64
|
import { openScreenCapture } from "@mediaway/device";
|
|
60
65
|
|
|
61
|
-
const screen = await openScreenCapture({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
const screen = await openScreenCapture({ timeBase: { num: 1, den: 30 }, monitorIndex: 0 });
|
|
67
|
+
console.log(`screen: ${screen.width}x${screen.height}`);
|
|
68
|
+
const frame = screen.pollFrame(); // VideoFrame | null — data is ALWAYS empty (see below)
|
|
69
|
+
await screen.close(); // also closes the internally-created GPU device
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`pollFrame()`'s `VideoFrame.data` is always empty — it proves frames are
|
|
73
|
+
genuinely arriving (real `pts`/geometry) but does not copy pixels out (no CPU
|
|
74
|
+
readback path for Screen). For real pixel data, feed the session straight
|
|
75
|
+
into an encoder with `EncodeSession.writeFrameFromDesktopCapture()`
|
|
76
|
+
(`@mediaway/encoder`), Zero-Copy end to end — see that package's README.
|
|
77
|
+
|
|
78
|
+
## GPU device factory
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { listGpuAdapters, GpuDevice, openScreenCapture } from "@mediaway/device";
|
|
82
|
+
|
|
83
|
+
const adapters = listGpuAdapters(); // name, vendorId, deviceId, dedicatedVideoMemoryBytes, isHardware
|
|
84
|
+
const device = await GpuDevice.create({ adapterIndex: adapters[0].index });
|
|
85
|
+
|
|
86
|
+
// Share one device across capture and encode (see @mediaway/encoder):
|
|
87
|
+
const screen = await openScreenCapture({ timeBase: { num: 1, den: 30 }, gpuDevice: device });
|
|
88
|
+
// ... screen.close() leaves `device` open since you supplied it — close it yourself:
|
|
66
89
|
await screen.close();
|
|
90
|
+
device.close();
|
|
67
91
|
```
|
|
68
92
|
|
|
93
|
+
Omit `adapterIndex` (or the whole `options` object) for the first hardware
|
|
94
|
+
adapter DXGI reports.
|
|
95
|
+
|
|
69
96
|
## API
|
|
70
97
|
|
|
71
98
|
| Function | Session | Frames |
|
|
72
99
|
| --- | --- | --- |
|
|
73
100
|
| `openCamera({ index, timeBase })` | `CameraSession` (`width`, `height`, `pixelFormat` = `"nv12"`) | `pollFrame(): VideoFrame \| null` |
|
|
74
101
|
| `openMicrophone({ sampleRate, channels })` | `MicSession` (`sampleRate`, `channels`) | `pollFrame(): AudioFrame \| null` |
|
|
75
|
-
| `openScreenCapture({ timeBase,
|
|
102
|
+
| `openScreenCapture({ timeBase, monitorIndex?, gpuDevice? })` | `ScreenSession` | `pollFrame(): VideoFrame \| null` (`data` always empty) |
|
|
103
|
+
| `listGpuAdapters()` | — | `GpuAdapterInfo[]` |
|
|
104
|
+
| `GpuDevice.create({ adapterIndex?, videoSupport?, debugLayer? })` | `GpuDevice` | — |
|
|
76
105
|
|
|
77
106
|
Every session's `close()` is async (joins the backend worker thread).
|
|
78
107
|
`CaptureUnavailableError` signals a missing device.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @mediaway/device — device capability: camera / screen / microphone capture
|
|
2
|
+
* @mediaway/device — device capability: camera / screen / microphone capture,
|
|
3
|
+
* plus the GPU device factory (mediaway-device ADR-0007).
|
|
3
4
|
*
|
|
4
5
|
* Implements the DX contract in bindings/nodejs/README.md over the
|
|
5
6
|
* mediaway-ffi C ABI (via @mediaway/ffi). The ABI is domain-split
|
|
6
|
-
* (`adr/0004-domain-feature-split.md`): this package folds it back into
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* (`adr/0004-domain-feature-split.md`): this package folds it back into DX
|
|
8
|
+
* functions. Camera delivers CPU frames; Screen capture is GPU-only, Zero-Copy
|
|
9
|
+
* — it needs a live GPU device handle with no CPU fallback
|
|
10
|
+
* (`adr/0003-gpu-handle-c-abi.md`), which `openScreenCapture()` now gets from
|
|
11
|
+
* `GpuDevice` (auto-created, or caller-supplied for explicit adapter control
|
|
12
|
+
* / sharing one device across capture + encode). Audio capture delivers raw
|
|
10
13
|
* interleaved f32le PCM — there is no audio encoder in the ABI.
|
|
11
14
|
*/
|
|
15
|
+
import { type RawGpuDeviceHandle } from "@mediaway/ffi";
|
|
12
16
|
import { MediawayError, type Rational } from "@mediaway/container";
|
|
13
17
|
export type { Rational } from "@mediaway/container";
|
|
18
|
+
/**
|
|
19
|
+
* @internal Key for the opaque native handle behind a capture/GPU-device
|
|
20
|
+
* session, consumed only by @mediaway/encoder's capture-to-encode bridge
|
|
21
|
+
* (`EncodeSession.writeFrameFromCameraCapture`/`writeFrameFromDesktopCapture`)
|
|
22
|
+
* and by `AutoVideoEncodeConfig.gpuDevice`. Not part of the public DX
|
|
23
|
+
* contract — never read this directly.
|
|
24
|
+
*/
|
|
25
|
+
export declare const NATIVE_HANDLE: unique symbol;
|
|
14
26
|
export interface VideoFrame {
|
|
15
27
|
pts: number;
|
|
16
28
|
duration: number;
|
|
@@ -46,6 +58,8 @@ export declare class CameraSession {
|
|
|
46
58
|
pollFrame(): VideoFrame | null;
|
|
47
59
|
/** Joins the backend worker thread — can block up to one frame interval. */
|
|
48
60
|
close(): Promise<void>;
|
|
61
|
+
/** @internal */
|
|
62
|
+
[NATIVE_HANDLE](): unknown;
|
|
49
63
|
}
|
|
50
64
|
/** Open camera `index` at `timeBase`; throws CaptureUnavailableError when no
|
|
51
65
|
* camera exists on this machine. */
|
|
@@ -71,25 +85,90 @@ export declare function openMicrophone(options: {
|
|
|
71
85
|
sampleRate: number;
|
|
72
86
|
channels?: number;
|
|
73
87
|
}): Promise<MicSession>;
|
|
88
|
+
export interface GpuAdapterInfo {
|
|
89
|
+
/** Pass to `GpuDevice.create({ adapterIndex })` to select this exact adapter. */
|
|
90
|
+
index: number;
|
|
91
|
+
name: string;
|
|
92
|
+
vendorId: number;
|
|
93
|
+
deviceId: number;
|
|
94
|
+
dedicatedVideoMemoryBytes: number;
|
|
95
|
+
/** `false` for a software rasterizer adapter (e.g. WARP). */
|
|
96
|
+
isHardware: boolean;
|
|
97
|
+
}
|
|
98
|
+
/** List every GPU adapter this machine's DXGI factory reports. */
|
|
99
|
+
export declare function listGpuAdapters(): GpuAdapterInfo[];
|
|
100
|
+
export interface GpuDeviceOptions {
|
|
101
|
+
/** Explicit adapter (from `listGpuAdapters()`'s `index`); omit for the first
|
|
102
|
+
* hardware adapter DXGI reports. */
|
|
103
|
+
adapterIndex?: number;
|
|
104
|
+
/** D3D11_CREATE_DEVICE_VIDEO_SUPPORT. Default true. */
|
|
105
|
+
videoSupport?: boolean;
|
|
106
|
+
/** D3D11_CREATE_DEVICE_DEBUG. Default false. */
|
|
107
|
+
debugLayer?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A real GPU device, created via the factory (no "bring your own device" path
|
|
111
|
+
* exists from JS). Pass into `openScreenCapture({ gpuDevice })` and/or
|
|
112
|
+
* `AutoVideoEncodeConfig.gpuDevice` (`@mediaway/encoder`) to share one device
|
|
113
|
+
* across capture and encode — Zero-Copy end to end. Close it after every
|
|
114
|
+
* session built from it is closed.
|
|
115
|
+
*/
|
|
116
|
+
export declare class GpuDevice {
|
|
117
|
+
private handle;
|
|
118
|
+
/** Wrap a native GPU device handle. Prefer the create() factory. */
|
|
119
|
+
private constructor();
|
|
120
|
+
/** Create a real GPU device. Throws CaptureUnavailableError when no usable
|
|
121
|
+
* adapter exists (e.g. headless CI without even WARP). */
|
|
122
|
+
static create(options?: GpuDeviceOptions): Promise<GpuDevice>;
|
|
123
|
+
/** Release the underlying GPU device. Every session built from it (Screen
|
|
124
|
+
* capture, encode config) becomes invalid the moment this returns. */
|
|
125
|
+
close(): void;
|
|
126
|
+
/** @internal — the `mediaway_gpu_device_handle_t` value bits, read once at
|
|
127
|
+
* create() time. Consumed by openScreenCapture() and
|
|
128
|
+
* AutoVideoEncodeConfig.gpuDevice (@mediaway/encoder). Not part of the
|
|
129
|
+
* public DX contract. */
|
|
130
|
+
[NATIVE_HANDLE](): RawGpuDeviceHandle;
|
|
131
|
+
}
|
|
74
132
|
/**
|
|
75
|
-
* A Screen capture session —
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
133
|
+
* A Screen capture session — GPU-only, Zero-Copy (`adr/0003-gpu-handle-c-abi.md`
|
|
134
|
+
* §4: no CPU fallback exists in the wrapped Rust backend, so unlike Camera
|
|
135
|
+
* there is no CPU pixel readback path here either). `pollFrame()` proves
|
|
136
|
+
* frames are genuinely arriving (real `pts`/geometry) but its `VideoFrame.data`
|
|
137
|
+
* is always empty — it does not copy pixels out. For real pixel data, feed
|
|
138
|
+
* the session straight into the encoder with
|
|
139
|
+
* `EncodeSession.writeFrameFromDesktopCapture()` (`@mediaway/encoder`), which
|
|
140
|
+
* moves the GPU texture Zero-Copy with no CPU round trip at all.
|
|
81
141
|
*/
|
|
82
142
|
export declare class ScreenSession {
|
|
83
|
-
readonly width
|
|
84
|
-
readonly height
|
|
143
|
+
readonly width: number;
|
|
144
|
+
readonly height: number;
|
|
85
145
|
readonly pixelFormat: "bgra8";
|
|
86
146
|
readonly timeBase: Rational;
|
|
87
|
-
|
|
88
|
-
|
|
147
|
+
private handle;
|
|
148
|
+
private readonly ownsGpuDevice;
|
|
149
|
+
/** Wrap a native desktop capture handle. Prefer the openScreenCapture() factory. */
|
|
150
|
+
constructor(handle: unknown, width: number, height: number, timeBase: Rational, ownsGpuDevice: GpuDevice | undefined);
|
|
151
|
+
/** Poll the next frame; null when nothing is ready yet. Sync, never blocks.
|
|
152
|
+
* `data` is always empty — see the class doc. */
|
|
89
153
|
pollFrame(): VideoFrame | null;
|
|
154
|
+
/** Joins the backend worker thread — can block up to one frame interval.
|
|
155
|
+
* Also closes the GPU device this session created internally (auto mode);
|
|
156
|
+
* a caller-supplied device (`options.gpuDevice`) is left open — the caller
|
|
157
|
+
* owns it and must close it themselves. */
|
|
90
158
|
close(): Promise<void>;
|
|
159
|
+
/** @internal */
|
|
160
|
+
[NATIVE_HANDLE](): unknown;
|
|
91
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Open Screen capture for output `monitorIndex` (default the primary
|
|
164
|
+
* display). Creates a `GpuDevice` internally unless `options.gpuDevice` is
|
|
165
|
+
* supplied — pass one in to pick an explicit adapter
|
|
166
|
+
* (`GpuDevice.create({ adapterIndex })`) or to share one device across
|
|
167
|
+
* capture and encode. Throws `CaptureUnavailableError` when no usable
|
|
168
|
+
* GPU/Desktop-Duplication path exists.
|
|
169
|
+
*/
|
|
92
170
|
export declare function openScreenCapture(options: {
|
|
93
171
|
timeBase: Rational;
|
|
94
172
|
monitorIndex?: number;
|
|
173
|
+
gpuDevice?: GpuDevice;
|
|
95
174
|
}): Promise<ScreenSession>;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @mediaway/device — device capability: camera / screen / microphone capture
|
|
2
|
+
* @mediaway/device — device capability: camera / screen / microphone capture,
|
|
3
|
+
* plus the GPU device factory (mediaway-device ADR-0007).
|
|
3
4
|
*
|
|
4
5
|
* Implements the DX contract in bindings/nodejs/README.md over the
|
|
5
6
|
* mediaway-ffi C ABI (via @mediaway/ffi). The ABI is domain-split
|
|
6
|
-
* (`adr/0004-domain-feature-split.md`): this package folds it back into
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* (`adr/0004-domain-feature-split.md`): this package folds it back into DX
|
|
8
|
+
* functions. Camera delivers CPU frames; Screen capture is GPU-only, Zero-Copy
|
|
9
|
+
* — it needs a live GPU device handle with no CPU fallback
|
|
10
|
+
* (`adr/0003-gpu-handle-c-abi.md`), which `openScreenCapture()` now gets from
|
|
11
|
+
* `GpuDevice` (auto-created, or caller-supplied for explicit adapter control
|
|
12
|
+
* / sharing one device across capture + encode). Audio capture delivers raw
|
|
10
13
|
* interleaved f32le PCM — there is no audio encoder in the ABI.
|
|
11
14
|
*/
|
|
12
|
-
import { MwAudioFrame, MwAudioConfig, MwCameraConfig, MwCameraFrame, device, copyBytes, } from "@mediaway/ffi";
|
|
15
|
+
import { MwAudioFrame, MwAudioConfig, MwCameraConfig, MwCameraFrame, MwGpuAdapterInfo, device, copyBytes, decodeArray, } from "@mediaway/ffi";
|
|
13
16
|
import { MediawayError } from "@mediaway/container";
|
|
17
|
+
/**
|
|
18
|
+
* @internal Key for the opaque native handle behind a capture/GPU-device
|
|
19
|
+
* session, consumed only by @mediaway/encoder's capture-to-encode bridge
|
|
20
|
+
* (`EncodeSession.writeFrameFromCameraCapture`/`writeFrameFromDesktopCapture`)
|
|
21
|
+
* and by `AutoVideoEncodeConfig.gpuDevice`. Not part of the public DX
|
|
22
|
+
* contract — never read this directly.
|
|
23
|
+
*/
|
|
24
|
+
export const NATIVE_HANDLE = Symbol("mediaway.device.nativeHandle");
|
|
14
25
|
/** No capture device/backend is available, or the ABI rejects this
|
|
15
26
|
* configuration as unsupported (today: Screen capture) — expected outcomes,
|
|
16
27
|
* not hard failures. */
|
|
@@ -82,6 +93,10 @@ export class CameraSession {
|
|
|
82
93
|
this.handle = null;
|
|
83
94
|
}
|
|
84
95
|
}
|
|
96
|
+
/** @internal */
|
|
97
|
+
[NATIVE_HANDLE]() {
|
|
98
|
+
return this.handle;
|
|
99
|
+
}
|
|
85
100
|
}
|
|
86
101
|
/** Open camera `index` at `timeBase`; throws CaptureUnavailableError when no
|
|
87
102
|
* camera exists on this machine. */
|
|
@@ -140,30 +155,163 @@ export async function openMicrophone(options) {
|
|
|
140
155
|
checkDevice(device.audioFormat(out[0], rate, channels));
|
|
141
156
|
return new MicSession(out[0], rate[0], channels[0]);
|
|
142
157
|
}
|
|
143
|
-
|
|
158
|
+
/** List every GPU adapter this machine's DXGI factory reports. */
|
|
159
|
+
export function listGpuAdapters() {
|
|
160
|
+
const outAdapters = [null];
|
|
161
|
+
const outCount = [0];
|
|
162
|
+
checkDevice(device.gpuAdapterList(outAdapters, outCount));
|
|
163
|
+
const count = outCount[0];
|
|
164
|
+
const decoded = decodeArray(outAdapters[0], MwGpuAdapterInfo, count);
|
|
165
|
+
const result = decoded.map((raw) => ({
|
|
166
|
+
index: raw.index,
|
|
167
|
+
name: raw.name,
|
|
168
|
+
vendorId: raw.vendor_id,
|
|
169
|
+
deviceId: raw.device_id,
|
|
170
|
+
dedicatedVideoMemoryBytes: Number(raw.dedicated_video_memory),
|
|
171
|
+
isHardware: raw.is_hardware,
|
|
172
|
+
}));
|
|
173
|
+
if (count > 0)
|
|
174
|
+
device.gpuAdapterListFree(outAdapters[0], count);
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A real GPU device, created via the factory (no "bring your own device" path
|
|
179
|
+
* exists from JS). Pass into `openScreenCapture({ gpuDevice })` and/or
|
|
180
|
+
* `AutoVideoEncodeConfig.gpuDevice` (`@mediaway/encoder`) to share one device
|
|
181
|
+
* across capture and encode — Zero-Copy end to end. Close it after every
|
|
182
|
+
* session built from it is closed.
|
|
183
|
+
*/
|
|
184
|
+
export class GpuDevice {
|
|
185
|
+
handle;
|
|
186
|
+
/** Wrap a native GPU device handle. Prefer the create() factory. */
|
|
187
|
+
constructor(handle) {
|
|
188
|
+
this.handle = handle;
|
|
189
|
+
}
|
|
190
|
+
/** Create a real GPU device. Throws CaptureUnavailableError when no usable
|
|
191
|
+
* adapter exists (e.g. headless CI without even WARP). */
|
|
192
|
+
static async create(options = {}) {
|
|
193
|
+
const raw = {
|
|
194
|
+
adapter: {
|
|
195
|
+
kind: options.adapterIndex === undefined ? 0 : 1,
|
|
196
|
+
index: options.adapterIndex ?? 0,
|
|
197
|
+
},
|
|
198
|
+
video_support: options.videoSupport ?? true,
|
|
199
|
+
debug_layer: options.debugLayer ?? false,
|
|
200
|
+
};
|
|
201
|
+
const out = [null];
|
|
202
|
+
checkDevice(device.gpuDeviceCreate(raw, out));
|
|
203
|
+
if (!out[0])
|
|
204
|
+
throw new MediawayError(9, "GPU device create returned no handle");
|
|
205
|
+
return new GpuDevice(out[0]);
|
|
206
|
+
}
|
|
207
|
+
/** Release the underlying GPU device. Every session built from it (Screen
|
|
208
|
+
* capture, encode config) becomes invalid the moment this returns. */
|
|
209
|
+
close() {
|
|
210
|
+
if (this.handle) {
|
|
211
|
+
device.gpuDeviceClose(this.handle);
|
|
212
|
+
this.handle = null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** @internal — the `mediaway_gpu_device_handle_t` value bits, read once at
|
|
216
|
+
* create() time. Consumed by openScreenCapture() and
|
|
217
|
+
* AutoVideoEncodeConfig.gpuDevice (@mediaway/encoder). Not part of the
|
|
218
|
+
* public DX contract. */
|
|
219
|
+
[NATIVE_HANDLE]() {
|
|
220
|
+
const out = {};
|
|
221
|
+
checkDevice(device.gpuDeviceHandle(this.handle, out));
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// ── Screen ───────────────────────────────────────────────────────────────────
|
|
144
226
|
/**
|
|
145
|
-
* A Screen capture session —
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
227
|
+
* A Screen capture session — GPU-only, Zero-Copy (`adr/0003-gpu-handle-c-abi.md`
|
|
228
|
+
* §4: no CPU fallback exists in the wrapped Rust backend, so unlike Camera
|
|
229
|
+
* there is no CPU pixel readback path here either). `pollFrame()` proves
|
|
230
|
+
* frames are genuinely arriving (real `pts`/geometry) but its `VideoFrame.data`
|
|
231
|
+
* is always empty — it does not copy pixels out. For real pixel data, feed
|
|
232
|
+
* the session straight into the encoder with
|
|
233
|
+
* `EncodeSession.writeFrameFromDesktopCapture()` (`@mediaway/encoder`), which
|
|
234
|
+
* moves the GPU texture Zero-Copy with no CPU round trip at all.
|
|
151
235
|
*/
|
|
152
236
|
export class ScreenSession {
|
|
153
|
-
width
|
|
154
|
-
height
|
|
237
|
+
width;
|
|
238
|
+
height;
|
|
155
239
|
pixelFormat = "bgra8";
|
|
156
240
|
timeBase;
|
|
157
|
-
|
|
158
|
-
|
|
241
|
+
handle;
|
|
242
|
+
ownsGpuDevice;
|
|
243
|
+
/** Wrap a native desktop capture handle. Prefer the openScreenCapture() factory. */
|
|
244
|
+
constructor(handle, width, height, timeBase, ownsGpuDevice) {
|
|
245
|
+
this.handle = handle;
|
|
246
|
+
this.width = width;
|
|
247
|
+
this.height = height;
|
|
159
248
|
this.timeBase = timeBase;
|
|
249
|
+
this.ownsGpuDevice = ownsGpuDevice;
|
|
160
250
|
}
|
|
251
|
+
/** Poll the next frame; null when nothing is ready yet. Sync, never blocks.
|
|
252
|
+
* `data` is always empty — see the class doc. */
|
|
161
253
|
pollFrame() {
|
|
162
|
-
|
|
254
|
+
const raw = {};
|
|
255
|
+
const has = [false];
|
|
256
|
+
checkDevice(device.desktopPollFrame(this.handle, raw, has));
|
|
257
|
+
if (!has[0])
|
|
258
|
+
return null;
|
|
259
|
+
// storage_kind is always GPU (1) for Screen — the frame lives in
|
|
260
|
+
// gpu_buffer, a BORROWED handle released below (never freed via
|
|
261
|
+
// desktopFrameFree, which would double-release it; there is nothing to
|
|
262
|
+
// copy out of raw.data/data_len, which stay empty for GPU storage).
|
|
263
|
+
checkDevice(device.desktopReleaseFrame(this.handle));
|
|
264
|
+
return {
|
|
265
|
+
pts: Number(raw.pts),
|
|
266
|
+
duration: Number(raw.duration),
|
|
267
|
+
width: raw.width,
|
|
268
|
+
height: raw.height,
|
|
269
|
+
pixelFormat: "bgra8",
|
|
270
|
+
data: Buffer.alloc(0),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/** Joins the backend worker thread — can block up to one frame interval.
|
|
274
|
+
* Also closes the GPU device this session created internally (auto mode);
|
|
275
|
+
* a caller-supplied device (`options.gpuDevice`) is left open — the caller
|
|
276
|
+
* owns it and must close it themselves. */
|
|
277
|
+
async close() {
|
|
278
|
+
if (this.handle) {
|
|
279
|
+
checkDevice(device.desktopClose(this.handle));
|
|
280
|
+
this.handle = null;
|
|
281
|
+
}
|
|
282
|
+
this.ownsGpuDevice?.close();
|
|
283
|
+
}
|
|
284
|
+
/** @internal */
|
|
285
|
+
[NATIVE_HANDLE]() {
|
|
286
|
+
return this.handle;
|
|
163
287
|
}
|
|
164
|
-
async close() { }
|
|
165
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Open Screen capture for output `monitorIndex` (default the primary
|
|
291
|
+
* display). Creates a `GpuDevice` internally unless `options.gpuDevice` is
|
|
292
|
+
* supplied — pass one in to pick an explicit adapter
|
|
293
|
+
* (`GpuDevice.create({ adapterIndex })`) or to share one device across
|
|
294
|
+
* capture and encode. Throws `CaptureUnavailableError` when no usable
|
|
295
|
+
* GPU/Desktop-Duplication path exists.
|
|
296
|
+
*/
|
|
166
297
|
export async function openScreenCapture(options) {
|
|
167
|
-
|
|
168
|
-
|
|
298
|
+
const ownsGpuDevice = options.gpuDevice === undefined ? await GpuDevice.create() : undefined;
|
|
299
|
+
const gpuDevice = options.gpuDevice ?? ownsGpuDevice;
|
|
300
|
+
if (gpuDevice === undefined)
|
|
301
|
+
throw new MediawayError(9, "no GPU device available");
|
|
302
|
+
try {
|
|
303
|
+
const config = device.desktopConfigScreen(options.monitorIndex ?? 0, { num: BigInt(options.timeBase.num), den: options.timeBase.den }, gpuDevice[NATIVE_HANDLE]());
|
|
304
|
+
const out = [null];
|
|
305
|
+
checkDevice(device.desktopOpen(config, out));
|
|
306
|
+
if (!out[0])
|
|
307
|
+
throw new MediawayError(9, "capture open returned no handle");
|
|
308
|
+
const width = [0];
|
|
309
|
+
const height = [0];
|
|
310
|
+
checkDevice(device.desktopGeometry(out[0], width, height));
|
|
311
|
+
return new ScreenSession(out[0], width[0], height[0], options.timeBase, ownsGpuDevice);
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
ownsGpuDevice?.close();
|
|
315
|
+
throw err;
|
|
316
|
+
}
|
|
169
317
|
}
|
package/package.json
CHANGED