@mediaway/encoder 0.1.4 → 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 CHANGED
@@ -8,6 +8,34 @@ session you push frames into.
8
8
  Windows is the verified platform. Software fallbacks and other platforms are
9
9
  explicit follow-up work — see the [Mediaway roadmap](https://github.com/nyxways/mediaway).
10
10
 
11
+ Video/audio **decode** moved to its own peer package, [`@mediaway/decoder`](../decoder)
12
+ — it is not re-exported here anymore.
13
+
14
+ ## Capture-to-encode bridge
15
+
16
+ `EncodeSession.writeFrameFromCameraCapture(capture)` /
17
+ `.writeFrameFromDesktopCapture(capture)` poll a `CameraSession`/`ScreenSession`
18
+ (`@mediaway/device`) and push straight into the session — no intermediate
19
+ `VideoFrame`, no CPU copy for Screen's GPU frames:
20
+
21
+ ```ts
22
+ import { AutoVideoEncodeConfig, openAutoEncoder } from "@mediaway/encoder";
23
+ import { GpuDevice, openScreenCapture } from "@mediaway/device";
24
+
25
+ const gpuDevice = await GpuDevice.create();
26
+ const screen = await openScreenCapture({ timeBase: { num: 1, den: 30 }, gpuDevice });
27
+
28
+ const config = AutoVideoEncodeConfig.defaults("h264", screen.width, screen.height, screen.timeBase);
29
+ config.gpuDevice = gpuDevice; // Zero-Copy GPU input, same device as capture
30
+ config.pixelFormat = "bgra8"; // DXGI Desktop Duplication's native format
31
+ const session = await openAutoEncoder(config);
32
+
33
+ while (recording) {
34
+ await session.writeFrameFromDesktopCapture(screen); // returns false if nothing was ready yet
35
+ }
36
+ const mp4 = await session.finish();
37
+ ```
38
+
11
39
  ## Install
12
40
 
13
41
  ```bash
@@ -57,8 +85,10 @@ const mp4 = await session.finish(); // terminal: complete fMP4 bytes
57
85
  | Member | Notes |
58
86
  | --- | --- |
59
87
  | `AutoVideoEncodeConfig.defaults(codec, width, height, timeBase)` | sensible defaults for the resolution; `codec` is `"h264" \| "hevc" \| "av1" \| "vp9"` |
88
+ | `AutoVideoEncodeConfig.gpuDevice` | a `GpuDevice` (`@mediaway/device`) for Zero-Copy GPU input; pair with `pixelFormat: "bgra8"` for Screen |
60
89
  | `openAutoEncoder(config)` | resolves a hardware backend or throws `EncoderUnavailableError` |
61
90
  | `EncodeSession.writeFrame(frame)` | async; the frame is copied — reuse the buffer |
91
+ | `EncodeSession.writeFrameFromCameraCapture(capture)` / `writeFrameFromDesktopCapture(capture)` | async; polls+pushes a capture session directly, `false` if nothing was ready |
62
92
  | `EncodeSession.finish()` | terminal; returns the complete fMP4 `Buffer` |
63
93
 
64
94
  ## License
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { type RawRational } from "@mediaway/ffi";
11
11
  import { MediawayError, type Rational } from "@mediaway/container";
12
+ import { type CameraSession, type GpuDevice, type ScreenSession } from "@mediaway/device";
12
13
  export type { Rational } from "@mediaway/container";
13
14
  export { MediawayError } from "@mediaway/container";
14
15
  export type PixelFormat = "nv12" | "bgra8" | "rgba8" | "i420" | "yuy2";
@@ -33,11 +34,18 @@ export declare class AutoVideoEncodeConfig {
33
34
  timeBase: Rational;
34
35
  bitrateBps?: number;
35
36
  pixelFormat?: PixelFormat;
37
+ /** Zero-Copy GPU input: the same device a Screen capture session was opened
38
+ * with (or any `GpuDevice`) — set together with `pixelFormat: "bgra8"` for
39
+ * `writeFrameFromDesktopCapture()`'s GPU frames (DXGI delivers BGRA8, not
40
+ * the NV12 default). Leave unset for CPU input (Camera, or manual
41
+ * `writeFrame()` with CPU-storage frames). */
42
+ gpuDevice?: GpuDevice;
36
43
  private constructor();
37
- /** The ABI's defaults (backend-default bitrate, NV12 input). */
44
+ /** The ABI's defaults (backend-default bitrate, NV12 input, CPU-only). */
38
45
  static defaults(codec: VideoCodec, width: number, height: number, timeBase: Rational): AutoVideoEncodeConfig;
39
46
  toAbi(): RawRational;
40
47
  }
48
+ export declare function checkPipeline(status: number, noBackendError?: new (status: number, message: string) => MediawayError): void;
41
49
  /**
42
50
  * A single-use encode session. `finish()` is terminal — it consumes the
43
51
  * session; `close()` after it is a no-op (idempotent), but is required to free
@@ -52,6 +60,22 @@ export declare class EncodeSession {
52
60
  private finished;
53
61
  constructor(config: AutoVideoEncodeConfig, encoderHandle: unknown);
54
62
  writeFrame(frame: VideoFrame): Promise<void>;
63
+ /**
64
+ * Poll one frame from `capture` (a `CameraSession`, `@mediaway/device`) and
65
+ * push it straight into this session — no intermediate `VideoFrame`, no
66
+ * extra copy (`adr/pipeline/0005-capture-encode-bridge-c-abi.md`). Returns
67
+ * `false` when nothing was ready yet (not an error) — mirrors
68
+ * `CameraSession.pollFrame()`'s own null-vs-error shape.
69
+ */
70
+ writeFrameFromCameraCapture(capture: CameraSession): Promise<boolean>;
71
+ /**
72
+ * Same shape as `writeFrameFromCameraCapture`, for a `ScreenSession`
73
+ * (`@mediaway/device`) instead of Camera. GPU frames pass through
74
+ * Zero-Copy: the polled frame's GPU handle moves straight into the encoder
75
+ * with no CPU copy — this is the real way to consume Screen frames (see
76
+ * `ScreenSession.pollFrame()`'s own doc: it never copies pixels out).
77
+ */
78
+ writeFrameFromDesktopCapture(capture: ScreenSession): Promise<boolean>;
55
79
  /** Flush the encoder + muxer and return the complete fMP4 bytes. Terminal. */
56
80
  finish(): Promise<Buffer>;
57
81
  /** Idempotent; no-op after finish(). Frees the native handle on error paths. */
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { MwAudioEncodeConfig, MwAudioFrameView, MwAudioPacket, MwAudioStreamInfo, MwPipelineFrame, MwRational, pipeline, copyBytes, } from "@mediaway/ffi";
11
11
  import { MediawayError } from "@mediaway/container";
12
+ import { NATIVE_HANDLE } from "@mediaway/device";
12
13
  export { MediawayError } from "@mediaway/container";
13
14
  /** No encode backend is openable for this config — expected on machines
14
15
  * without a usable encoder; catch it and exit gracefully. */
@@ -21,13 +22,19 @@ export class AutoVideoEncodeConfig {
21
22
  timeBase;
22
23
  bitrateBps; // 0 / undefined = backend default
23
24
  pixelFormat;
25
+ /** Zero-Copy GPU input: the same device a Screen capture session was opened
26
+ * with (or any `GpuDevice`) — set together with `pixelFormat: "bgra8"` for
27
+ * `writeFrameFromDesktopCapture()`'s GPU frames (DXGI delivers BGRA8, not
28
+ * the NV12 default). Leave unset for CPU input (Camera, or manual
29
+ * `writeFrame()` with CPU-storage frames). */
30
+ gpuDevice;
24
31
  constructor(codec, width, height, timeBase) {
25
32
  this.codec = codec;
26
33
  this.width = width;
27
34
  this.height = height;
28
35
  this.timeBase = timeBase;
29
36
  }
30
- /** The ABI's defaults (backend-default bitrate, NV12 input). */
37
+ /** The ABI's defaults (backend-default bitrate, NV12 input, CPU-only). */
31
38
  static defaults(codec, width, height, timeBase) {
32
39
  return new AutoVideoEncodeConfig(codec, width, height, timeBase);
33
40
  }
@@ -39,14 +46,16 @@ export class AutoVideoEncodeConfig {
39
46
  const fmtMap = { nv12: 0, i420: 1, bgra8: 2, rgba8: 3, yuy2: 4 };
40
47
  if (this.pixelFormat !== undefined)
41
48
  raw.pixel_format = fmtMap[this.pixelFormat] ?? 0;
49
+ if (this.gpuDevice !== undefined)
50
+ raw.gpu_device = this.gpuDevice[NATIVE_HANDLE]();
42
51
  return raw;
43
52
  }
44
53
  }
45
- function checkPipeline(status) {
54
+ export function checkPipeline(status, noBackendError = EncoderUnavailableError) {
46
55
  if (status === 0)
47
56
  return;
48
57
  if (status === 3)
49
- throw new EncoderUnavailableError(status, "no encode backend compiled in or openable");
58
+ throw new noBackendError(status, "no backend compiled in or openable");
50
59
  const names = {
51
60
  1: "invalid argument",
52
61
  2: "handle poisoned by an earlier panic",
@@ -59,6 +68,8 @@ function checkPipeline(status) {
59
68
  10: "malformed container data",
60
69
  11: "unknown error",
61
70
  12: "internal panic (handle poisoned)",
71
+ 13: "decoder backend OS/API failure",
72
+ 14: "decode session already finished or not open",
62
73
  };
63
74
  throw new MediawayError(status, names[status] ?? "unknown pipeline error");
64
75
  }
@@ -98,6 +109,30 @@ export class EncodeSession {
98
109
  };
99
110
  checkPipeline(pipeline.sessionWriteFrame(this.handle, raw));
100
111
  }
112
+ /**
113
+ * Poll one frame from `capture` (a `CameraSession`, `@mediaway/device`) and
114
+ * push it straight into this session — no intermediate `VideoFrame`, no
115
+ * extra copy (`adr/pipeline/0005-capture-encode-bridge-c-abi.md`). Returns
116
+ * `false` when nothing was ready yet (not an error) — mirrors
117
+ * `CameraSession.pollFrame()`'s own null-vs-error shape.
118
+ */
119
+ async writeFrameFromCameraCapture(capture) {
120
+ const wrote = [false];
121
+ checkPipeline(pipeline.sessionWriteFrameFromCameraCapture(this.handle, capture[NATIVE_HANDLE](), wrote));
122
+ return wrote[0];
123
+ }
124
+ /**
125
+ * Same shape as `writeFrameFromCameraCapture`, for a `ScreenSession`
126
+ * (`@mediaway/device`) instead of Camera. GPU frames pass through
127
+ * Zero-Copy: the polled frame's GPU handle moves straight into the encoder
128
+ * with no CPU copy — this is the real way to consume Screen frames (see
129
+ * `ScreenSession.pollFrame()`'s own doc: it never copies pixels out).
130
+ */
131
+ async writeFrameFromDesktopCapture(capture) {
132
+ const wrote = [false];
133
+ checkPipeline(pipeline.sessionWriteFrameFromDesktopCapture(this.handle, capture[NATIVE_HANDLE](), wrote));
134
+ return wrote[0];
135
+ }
101
136
  /** Flush the encoder + muxer and return the complete fMP4 bytes. Terminal. */
102
137
  async finish() {
103
138
  const outData = [null];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mediaway/encoder",
3
- "version": "0.1.4",
4
- "description": "Automatic hardware video encoding to fragmented MP4 for Node.js (NVENC / Vulkan Video / QuickSync)",
3
+ "version": "0.1.6",
4
+ "description": "Rust-based, lightweight, fast — automatic hardware video encoding to fragmented MP4 for Node.js (NVENC / Vulkan Video / QuickSync)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -26,8 +26,9 @@
26
26
  "build": "tsc -p tsconfig.json"
27
27
  },
28
28
  "dependencies": {
29
- "@mediaway/ffi": "0.1.0",
30
- "@mediaway/container": "0.1.0"
29
+ "@mediaway/ffi": "^0.1.0",
30
+ "@mediaway/container": "^0.1.0",
31
+ "@mediaway/device": "^0.1.0"
31
32
  },
32
33
  "devDependencies": {
33
34
  "typescript": "^5.6.0"