@pproenca/node-webcodecs 0.1.1-alpha.0 → 0.1.1-alpha.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.
Files changed (97) hide show
  1. package/README.md +75 -233
  2. package/binding.gyp +123 -0
  3. package/dist/audio-decoder.js +1 -2
  4. package/dist/audio-encoder.d.ts +4 -0
  5. package/dist/audio-encoder.js +28 -2
  6. package/dist/binding.d.ts +0 -2
  7. package/dist/binding.js +43 -125
  8. package/dist/control-message-queue.js +0 -1
  9. package/dist/demuxer.d.ts +7 -0
  10. package/dist/demuxer.js +9 -0
  11. package/dist/encoded-chunks.d.ts +16 -0
  12. package/dist/encoded-chunks.js +82 -2
  13. package/dist/image-decoder.js +4 -0
  14. package/dist/index.d.ts +11 -0
  15. package/dist/index.js +3 -1
  16. package/dist/native-types.d.ts +20 -0
  17. package/dist/platform.d.ts +1 -10
  18. package/dist/platform.js +1 -39
  19. package/dist/resource-manager.d.ts +1 -2
  20. package/dist/resource-manager.js +3 -17
  21. package/dist/types.d.ts +12 -0
  22. package/dist/video-decoder.d.ts +21 -0
  23. package/dist/video-decoder.js +74 -2
  24. package/dist/video-encoder.d.ts +22 -0
  25. package/dist/video-encoder.js +83 -8
  26. package/lib/audio-decoder.ts +1 -2
  27. package/lib/audio-encoder.ts +31 -2
  28. package/lib/binding.ts +45 -104
  29. package/lib/control-message-queue.ts +0 -1
  30. package/lib/demuxer.ts +10 -0
  31. package/lib/encoded-chunks.ts +90 -2
  32. package/lib/image-decoder.ts +5 -0
  33. package/lib/index.ts +3 -0
  34. package/lib/native-types.ts +22 -0
  35. package/lib/platform.ts +1 -41
  36. package/lib/resource-manager.ts +3 -19
  37. package/lib/types.ts +13 -0
  38. package/lib/video-decoder.ts +84 -2
  39. package/lib/video-encoder.ts +90 -8
  40. package/package.json +49 -32
  41. package/src/addon.cc +57 -0
  42. package/src/async_decode_worker.cc +241 -33
  43. package/src/async_decode_worker.h +55 -3
  44. package/src/async_encode_worker.cc +103 -35
  45. package/src/async_encode_worker.h +23 -4
  46. package/src/audio_data.cc +38 -15
  47. package/src/audio_data.h +1 -0
  48. package/src/audio_decoder.cc +24 -3
  49. package/src/audio_encoder.cc +55 -4
  50. package/src/common.cc +125 -17
  51. package/src/common.h +34 -4
  52. package/src/demuxer.cc +16 -2
  53. package/src/encoded_audio_chunk.cc +10 -0
  54. package/src/encoded_audio_chunk.h +2 -0
  55. package/src/encoded_video_chunk.h +1 -0
  56. package/src/error_builder.cc +0 -4
  57. package/src/image_decoder.cc +127 -90
  58. package/src/image_decoder.h +11 -4
  59. package/src/muxer.cc +1 -0
  60. package/src/test_video_generator.cc +3 -2
  61. package/src/video_decoder.cc +169 -19
  62. package/src/video_decoder.h +9 -11
  63. package/src/video_encoder.cc +389 -32
  64. package/src/video_encoder.h +15 -0
  65. package/src/video_filter.cc +22 -11
  66. package/src/video_frame.cc +160 -5
  67. package/src/warnings.cc +0 -4
  68. package/dist/audio-data.js.map +0 -1
  69. package/dist/audio-decoder.js.map +0 -1
  70. package/dist/audio-encoder.js.map +0 -1
  71. package/dist/binding.js.map +0 -1
  72. package/dist/codec-base.js.map +0 -1
  73. package/dist/control-message-queue.js.map +0 -1
  74. package/dist/demuxer.js.map +0 -1
  75. package/dist/encoded-chunks.js.map +0 -1
  76. package/dist/errors.js.map +0 -1
  77. package/dist/ffmpeg.d.ts +0 -21
  78. package/dist/ffmpeg.js +0 -112
  79. package/dist/image-decoder.js.map +0 -1
  80. package/dist/image-track-list.js.map +0 -1
  81. package/dist/image-track.js.map +0 -1
  82. package/dist/index.js.map +0 -1
  83. package/dist/is.js.map +0 -1
  84. package/dist/muxer.js.map +0 -1
  85. package/dist/native-types.js.map +0 -1
  86. package/dist/platform.js.map +0 -1
  87. package/dist/resource-manager.js.map +0 -1
  88. package/dist/test-video-generator.js.map +0 -1
  89. package/dist/transfer.js.map +0 -1
  90. package/dist/types.js.map +0 -1
  91. package/dist/video-decoder.js.map +0 -1
  92. package/dist/video-encoder.js.map +0 -1
  93. package/dist/video-filter.js.map +0 -1
  94. package/dist/video-frame.js.map +0 -1
  95. package/install/build.js +0 -51
  96. package/install/check.js +0 -192
  97. package/lib/ffmpeg.ts +0 -78
package/dist/binding.js CHANGED
@@ -2,140 +2,58 @@
2
2
  // Copyright 2024 The node-webcodecs Authors
3
3
  // SPDX-License-Identifier: MIT
4
4
  //
5
- // Native binding loader with fallback chain and enhanced error messages.
6
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
- if (k2 === undefined) k2 = k;
8
- var desc = Object.getOwnPropertyDescriptor(m, k);
9
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
- desc = { enumerable: true, get: function() { return m[k]; } };
11
- }
12
- Object.defineProperty(o, k2, desc);
13
- }) : (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- o[k2] = m[k];
16
- }));
17
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
- Object.defineProperty(o, "default", { enumerable: true, value: v });
19
- }) : function(o, v) {
20
- o["default"] = v;
21
- });
22
- var __importStar = (this && this.__importStar) || (function () {
23
- var ownKeys = function(o) {
24
- ownKeys = Object.getOwnPropertyNames || function (o) {
25
- var ar = [];
26
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
- return ar;
28
- };
29
- return ownKeys(o);
30
- };
31
- return function (mod) {
32
- if (mod && mod.__esModule) return mod;
33
- var result = {};
34
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
- __setModuleDefault(result, mod);
36
- return result;
37
- };
38
- })();
5
+ // Native binding loader using esbuild-style platform resolution.
6
+ // Tries platform-specific package first, falls back to node-gyp-build for local dev.
39
7
  Object.defineProperty(exports, "__esModule", { value: true });
40
8
  exports.platformInfo = exports.binding = void 0;
41
- const fs = __importStar(require("node:fs"));
42
- const path = __importStar(require("node:path"));
43
- const platform_1 = require("./platform");
44
- const rootDir = path.resolve(__dirname, '..');
45
- const candidates = [
46
- // Development build (node-gyp output)
47
- path.join(rootDir, 'build', 'Release', 'node_webcodecs.node'),
48
- path.join(rootDir, 'build', 'Debug', 'node_webcodecs.node'),
49
- // node-gyp-build compatible
50
- () => {
51
- try {
52
- return require('node-gyp-build')(rootDir);
53
- }
54
- catch {
55
- throw new Error('node-gyp-build not available');
56
- }
57
- },
58
- // Prebuilt from platform package (sharp pattern: @scope/pkg-platform/binding.node)
59
- () => {
60
- const pkg = (0, platform_1.getPrebuiltPackageName)();
61
- const bindingPath = `${pkg}/node-webcodecs.node`;
62
- return require(bindingPath);
63
- },
64
- ];
65
- function getPlatformBuildInstructions() {
66
- const platform = process.platform;
67
- if (platform === 'darwin') {
68
- return ` brew install ffmpeg pkg-config
69
- npm run build:native`;
70
- }
71
- if (platform === 'linux') {
72
- return ` sudo apt-get install libavcodec-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev pkg-config
73
- npm run build:native`;
74
- }
75
- if (platform === 'win32') {
76
- return ` Download FFmpeg from https://github.com/BtbN/FFmpeg-Builds/releases
77
- Set FFMPEG_PATH environment variable
78
- npm run build:native`;
79
- }
80
- return ` Install FFmpeg development libraries
81
- npm run build:native`;
82
- }
83
- function buildHelpMessage(errors) {
84
- const platform = (0, platform_1.runtimePlatformArch)();
85
- const hasPrebuilt = (0, platform_1.isPrebuiltAvailable)();
86
- let msg = `Could not load native binding for ${platform}.\n\n`;
87
- msg += `Node.js: ${process.version}\n\n`;
88
- msg += 'Attempted paths:\n';
89
- for (const { path: p, error } of errors) {
90
- msg += ` - ${p}: ${error.message}\n`;
91
- }
92
- msg += '\nPossible solutions:\n';
93
- if (hasPrebuilt) {
94
- msg += ' 1. Install with optional dependencies:\n';
95
- msg += ' npm install --include=optional\n\n';
96
- msg += ' 2. Build from source:\n';
97
- }
98
- else {
99
- msg += ' 1. Build from source:\n';
100
- }
101
- msg += getPlatformBuildInstructions();
102
- return msg;
103
- }
9
+ const node_path_1 = require("node:path");
10
+ const PLATFORMS = {
11
+ 'darwin-arm64': '@pproenca/node-webcodecs-darwin-arm64',
12
+ 'darwin-x64': '@pproenca/node-webcodecs-darwin-x64',
13
+ 'linux-x64': '@pproenca/node-webcodecs-linux-x64',
14
+ };
15
+ /**
16
+ * Load the native binding.
17
+ *
18
+ * Resolution order:
19
+ * 1. Platform-specific npm package (production path via optionalDependencies)
20
+ * 2. node-gyp-build (local development fallback)
21
+ */
104
22
  function loadBinding() {
105
- const errors = [];
106
- for (const candidate of candidates) {
107
- try {
108
- if (typeof candidate === 'function') {
109
- const binding = candidate();
110
- if (binding && typeof binding.VideoEncoder === 'function') {
111
- return binding;
112
- }
113
- throw new Error('Invalid binding: missing VideoEncoder');
114
- }
115
- if (!fs.existsSync(candidate)) {
116
- continue;
117
- }
118
- const binding = require(candidate);
119
- if (typeof binding.VideoEncoder !== 'function') {
120
- throw new Error('Invalid binding: missing VideoEncoder');
121
- }
122
- return binding;
123
- }
124
- catch (err) {
125
- errors.push({
126
- path: typeof candidate === 'string' ? candidate : 'dynamic loader',
127
- error: err instanceof Error ? err : new Error(String(err)),
128
- });
129
- }
23
+ const platform = `${process.platform}-${process.arch}`;
24
+ const pkg = PLATFORMS[platform];
25
+ if (!pkg) {
26
+ throw new Error(`Unsupported platform: ${platform}. ` +
27
+ `Supported platforms: ${Object.keys(PLATFORMS).join(', ')}`);
28
+ }
29
+ // Try platform-specific package first (production path)
30
+ try {
31
+ const pkgPath = require.resolve(`${pkg}/package.json`);
32
+ const binPath = (0, node_path_1.join)((0, node_path_1.dirname)(pkgPath), 'bin', 'node.napi.node');
33
+ return require(binPath);
34
+ }
35
+ catch {
36
+ // Platform package not installed - fallback to local build
37
+ }
38
+ // Fallback to node-gyp-build for local development
39
+ try {
40
+ const nodeGypBuild = require('node-gyp-build');
41
+ const rootDir = (0, node_path_1.resolve)(__dirname, '..');
42
+ return nodeGypBuild(rootDir);
43
+ }
44
+ catch (err) {
45
+ throw new Error(`Could not load the node-webcodecs native binding for ${platform}.\n` +
46
+ `Error: ${err instanceof Error ? err.message : String(err)}\n\n` +
47
+ `Solutions:\n` +
48
+ ` 1. Install the main package: npm install @pproenca/node-webcodecs\n` +
49
+ ` 2. Build from source: npm rebuild --build-from-source\n` +
50
+ ` 3. Ensure FFmpeg dev libs: pkg-config --exists libavcodec\n`);
130
51
  }
131
- throw new Error(buildHelpMessage(errors));
132
52
  }
133
53
  exports.binding = loadBinding();
134
54
  exports.platformInfo = {
135
55
  platform: process.platform,
136
56
  arch: process.arch,
137
- runtimePlatform: (0, platform_1.runtimePlatformArch)(),
138
57
  nodeVersion: process.version,
139
58
  napiVersion: process.versions.napi ?? 'unknown',
140
- prebuiltAvailable: (0, platform_1.isPrebuiltAvailable)(),
141
59
  };
@@ -71,7 +71,6 @@ class ControlMessageQueue {
71
71
  }
72
72
  }
73
73
  this.processing = false;
74
- // Continue processing if more messages
75
74
  if (this.queue.length > 0) {
76
75
  this.scheduleProcessing();
77
76
  }
package/dist/demuxer.d.ts CHANGED
@@ -4,6 +4,13 @@ export declare class Demuxer {
4
4
  constructor(init: DemuxerInit);
5
5
  open(path: string): Promise<void>;
6
6
  demux(): Promise<void>;
7
+ /**
8
+ * Read packets from the file in chunks.
9
+ * This is useful for yielding to the event loop during demuxing.
10
+ * @param maxPackets - Maximum number of packets to read. 0 = unlimited (reads all).
11
+ * @returns The number of packets actually read.
12
+ */
13
+ demuxPackets(maxPackets?: number): number;
7
14
  close(): void;
8
15
  getVideoTrack(): TrackInfo | null;
9
16
  getAudioTrack(): TrackInfo | null;
package/dist/demuxer.js CHANGED
@@ -30,6 +30,15 @@ class Demuxer {
30
30
  async demux() {
31
31
  return this._native.demux();
32
32
  }
33
+ /**
34
+ * Read packets from the file in chunks.
35
+ * This is useful for yielding to the event loop during demuxing.
36
+ * @param maxPackets - Maximum number of packets to read. 0 = unlimited (reads all).
37
+ * @returns The number of packets actually read.
38
+ */
39
+ demuxPackets(maxPackets) {
40
+ return this._native.demuxPackets(maxPackets ?? 0);
41
+ }
33
42
  close() {
34
43
  this._native.close();
35
44
  }
@@ -9,11 +9,22 @@ export declare class EncodedVideoChunk {
9
9
  /** @internal */
10
10
  _native: NativeEncodedVideoChunk;
11
11
  constructor(init: EncodedVideoChunkInit);
12
+ /**
13
+ * @internal
14
+ * Wrap an existing native EncodedVideoChunk without copying data.
15
+ * Used by the encoder's async output path to avoid double-copying.
16
+ */
17
+ static _fromNative(nativeChunk: NativeEncodedVideoChunk): EncodedVideoChunk;
12
18
  get type(): 'key' | 'delta';
13
19
  get timestamp(): number;
14
20
  get duration(): number | null;
15
21
  get byteLength(): number;
16
22
  copyTo(destination: ArrayBuffer | ArrayBufferView): void;
23
+ /**
24
+ * Releases the internal data buffer.
25
+ * Per W3C WebCodecs spec, this allows early release of memory.
26
+ */
27
+ close(): void;
17
28
  }
18
29
  export declare class EncodedAudioChunk {
19
30
  private _native;
@@ -23,5 +34,10 @@ export declare class EncodedAudioChunk {
23
34
  get duration(): number | null;
24
35
  get byteLength(): number;
25
36
  copyTo(destination: ArrayBuffer | ArrayBufferView): void;
37
+ /**
38
+ * Releases the internal data buffer.
39
+ * Per W3C WebCodecs spec, this allows early release of memory.
40
+ */
41
+ close(): void;
26
42
  get _nativeChunk(): NativeEncodedAudioChunk;
27
43
  }
@@ -7,15 +7,45 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.EncodedAudioChunk = exports.EncodedVideoChunk = void 0;
9
9
  const binding_1 = require("./binding");
10
+ const transfer_1 = require("./transfer");
10
11
  // Load native addon with type assertion
11
12
  const native = binding_1.binding;
13
+ /**
14
+ * FinalizationRegistry for automatic cleanup of native EncodedVideoChunk objects.
15
+ * When a JS EncodedVideoChunk wrapper becomes unreachable, the registry callback
16
+ * fires and releases the native memory via close().
17
+ *
18
+ * This provides a safety net for users who forget to call close(), preventing
19
+ * memory leaks in high-throughput scenarios where GC may be delayed.
20
+ */
21
+ const videoChunkRegistry = new FinalizationRegistry((native) => {
22
+ // The weak reference to the JS wrapper is now dead, but the native object
23
+ // may still be valid. Call close() to release its internal data buffer.
24
+ // close() is idempotent - safe to call even if already closed.
25
+ try {
26
+ native.close();
27
+ }
28
+ catch {
29
+ // Ignore errors - native object may already be destroyed
30
+ }
31
+ });
32
+ /**
33
+ * FinalizationRegistry for automatic cleanup of native EncodedAudioChunk objects.
34
+ */
35
+ const audioChunkRegistry = new FinalizationRegistry((native) => {
36
+ try {
37
+ native.close();
38
+ }
39
+ catch {
40
+ // Ignore errors - native object may already be destroyed
41
+ }
42
+ });
12
43
  class EncodedVideoChunk {
13
44
  constructor(init) {
14
45
  // W3C spec: type must be 'key' or 'delta'
15
46
  if (init.type !== 'key' && init.type !== 'delta') {
16
47
  throw new TypeError(`Invalid type: ${init.type}`);
17
48
  }
18
- // Convert BufferSource to Buffer for native
19
49
  let dataBuffer;
20
50
  if (init.data instanceof ArrayBuffer) {
21
51
  dataBuffer = Buffer.from(init.data);
@@ -32,6 +62,26 @@ class EncodedVideoChunk {
32
62
  duration: init.duration,
33
63
  data: dataBuffer,
34
64
  });
65
+ // Register with FinalizationRegistry for automatic cleanup.
66
+ // When this JS wrapper is GC'd, the registry callback will call close()
67
+ // on the native object to release memory.
68
+ videoChunkRegistry.register(this, this._native, this);
69
+ // Handle ArrayBuffer transfer semantics per W3C spec
70
+ if (init.transfer && Array.isArray(init.transfer)) {
71
+ (0, transfer_1.detachArrayBuffers)(init.transfer.filter((b) => b instanceof ArrayBuffer));
72
+ }
73
+ }
74
+ /**
75
+ * @internal
76
+ * Wrap an existing native EncodedVideoChunk without copying data.
77
+ * Used by the encoder's async output path to avoid double-copying.
78
+ */
79
+ static _fromNative(nativeChunk) {
80
+ const chunk = Object.create(EncodedVideoChunk.prototype);
81
+ chunk._native = nativeChunk;
82
+ // Register with FinalizationRegistry for automatic cleanup
83
+ videoChunkRegistry.register(chunk, nativeChunk, chunk);
84
+ return chunk;
35
85
  }
36
86
  get type() {
37
87
  return this._native.type;
@@ -62,11 +112,24 @@ class EncodedVideoChunk {
62
112
  throw new TypeError('Destination must be ArrayBuffer or ArrayBufferView');
63
113
  }
64
114
  }
115
+ /**
116
+ * Releases the internal data buffer.
117
+ * Per W3C WebCodecs spec, this allows early release of memory.
118
+ */
119
+ close() {
120
+ // Unregister from FinalizationRegistry to prevent double-close.
121
+ // If close() is called explicitly, we don't need the registry callback.
122
+ videoChunkRegistry.unregister(this);
123
+ this._native.close();
124
+ }
65
125
  }
66
126
  exports.EncodedVideoChunk = EncodedVideoChunk;
67
127
  class EncodedAudioChunk {
68
128
  constructor(init) {
69
- // Convert BufferSource to Buffer for native
129
+ // W3C spec: type must be 'key' or 'delta'
130
+ if (init.type !== 'key' && init.type !== 'delta') {
131
+ throw new TypeError(`Invalid type: ${init.type}`);
132
+ }
70
133
  let dataBuffer;
71
134
  if (init.data instanceof ArrayBuffer) {
72
135
  dataBuffer = Buffer.from(init.data);
@@ -83,6 +146,14 @@ class EncodedAudioChunk {
83
146
  duration: init.duration,
84
147
  data: dataBuffer,
85
148
  });
149
+ // Register with FinalizationRegistry for automatic cleanup.
150
+ // When this JS wrapper is GC'd, the registry callback will call close()
151
+ // on the native object to release memory.
152
+ audioChunkRegistry.register(this, this._native, this);
153
+ // Handle ArrayBuffer transfer semantics per W3C spec
154
+ if (init.transfer && Array.isArray(init.transfer)) {
155
+ (0, transfer_1.detachArrayBuffers)(init.transfer.filter((b) => b instanceof ArrayBuffer));
156
+ }
86
157
  }
87
158
  get type() {
88
159
  return this._native.type;
@@ -106,6 +177,15 @@ class EncodedAudioChunk {
106
177
  this._native.copyTo(uint8);
107
178
  }
108
179
  }
180
+ /**
181
+ * Releases the internal data buffer.
182
+ * Per W3C WebCodecs spec, this allows early release of memory.
183
+ */
184
+ close() {
185
+ // Unregister from FinalizationRegistry to prevent double-close.
186
+ audioChunkRegistry.unregister(this);
187
+ this._native.close();
188
+ }
109
189
  get _nativeChunk() {
110
190
  return this._native;
111
191
  }
@@ -36,6 +36,7 @@ class ImageDecoder {
36
36
  this._initOptions = {
37
37
  type: init.type,
38
38
  colorSpaceConversion: init.colorSpaceConversion,
39
+ premultiplyAlpha: init.premultiplyAlpha,
39
40
  desiredWidth: init.desiredWidth,
40
41
  desiredHeight: init.desiredHeight,
41
42
  preferAnimation: init.preferAnimation,
@@ -74,6 +75,9 @@ class ImageDecoder {
74
75
  if ('colorSpaceConversion' in init && init.colorSpaceConversion) {
75
76
  nativeInit.colorSpaceConversion = init.colorSpaceConversion;
76
77
  }
78
+ if ('premultiplyAlpha' in init && init.premultiplyAlpha !== undefined) {
79
+ nativeInit.premultiplyAlpha = init.premultiplyAlpha;
80
+ }
77
81
  if ('desiredWidth' in init && init.desiredWidth !== undefined) {
78
82
  nativeInit.desiredWidth = init.desiredWidth;
79
83
  }
package/dist/index.d.ts CHANGED
@@ -51,6 +51,17 @@ export declare const createEncoderConfigDescriptor: (config: object) => {
51
51
  colorMatrix: string;
52
52
  colorFullRange: boolean;
53
53
  };
54
+ export declare const getCounters: () => {
55
+ videoFrames: number;
56
+ audioData: number;
57
+ videoEncoders: number;
58
+ videoDecoders: number;
59
+ audioEncoders: number;
60
+ audioDecoders: number;
61
+ queue: number;
62
+ process: number;
63
+ frames: number;
64
+ };
54
65
  export type { ErrorCodeType } from './errors';
55
66
  export { AllocationError, ConfigurationError, DecodingError, EncodingError, ErrorCode, ffmpegErrorMessage, InvalidDataError, InvalidStateError, UnsupportedCodecError, WebCodecsError, } from './errors';
56
67
  export { ImageTrack } from './image-track';
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@
16
16
  * - SVC temporal layer tracking via scalabilityMode (L1T1, L1T2, L1T3)
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.TestVideoGenerator = exports.ResourceManager = exports.ImageTrackList = exports.ImageTrack = exports.WebCodecsError = exports.UnsupportedCodecError = exports.InvalidStateError = exports.InvalidDataError = exports.ffmpegErrorMessage = exports.ErrorCode = exports.EncodingError = exports.DecodingError = exports.ConfigurationError = exports.AllocationError = exports.createEncoderConfigDescriptor = exports.testAttrAsEnum = exports.ErrorBuilder = exports.clearFFmpegWarnings = exports.getFFmpegWarnings = exports.WarningAccumulator = exports.VideoFrame = exports.VideoColorSpace = exports.VideoFilter = exports.VideoEncoder = exports.VideoDecoder = exports.Muxer = exports.ImageDecoder = exports.EncodedVideoChunk = exports.EncodedAudioChunk = exports.Demuxer = exports.AudioEncoder = exports.AudioDecoder = exports.AudioData = exports.platformInfo = void 0;
19
+ exports.TestVideoGenerator = exports.ResourceManager = exports.ImageTrackList = exports.ImageTrack = exports.WebCodecsError = exports.UnsupportedCodecError = exports.InvalidStateError = exports.InvalidDataError = exports.ffmpegErrorMessage = exports.ErrorCode = exports.EncodingError = exports.DecodingError = exports.ConfigurationError = exports.AllocationError = exports.getCounters = exports.createEncoderConfigDescriptor = exports.testAttrAsEnum = exports.ErrorBuilder = exports.clearFFmpegWarnings = exports.getFFmpegWarnings = exports.WarningAccumulator = exports.VideoFrame = exports.VideoColorSpace = exports.VideoFilter = exports.VideoEncoder = exports.VideoDecoder = exports.Muxer = exports.ImageDecoder = exports.EncodedVideoChunk = exports.EncodedAudioChunk = exports.Demuxer = exports.AudioEncoder = exports.AudioDecoder = exports.AudioData = exports.platformInfo = void 0;
20
20
  const binding_1 = require("./binding");
21
21
  Object.defineProperty(exports, "platformInfo", { enumerable: true, get: function () { return binding_1.platformInfo; } });
22
22
  // Load native addon with type assertion
@@ -54,6 +54,8 @@ exports.clearFFmpegWarnings = native.clearFFmpegWarnings;
54
54
  exports.ErrorBuilder = native.ErrorBuilder;
55
55
  exports.testAttrAsEnum = native.testAttrAsEnum;
56
56
  exports.createEncoderConfigDescriptor = native.createEncoderConfigDescriptor;
57
+ // Export instance counters for monitoring and leak detection
58
+ exports.getCounters = native.getCounters;
57
59
  // Re-export error classes and codes
58
60
  var errors_1 = require("./errors");
59
61
  Object.defineProperty(exports, "AllocationError", { enumerable: true, get: function () { return errors_1.AllocationError; } });
@@ -65,6 +65,7 @@ export interface NativeVideoDecoder {
65
65
  readonly state: CodecState;
66
66
  readonly decodeQueueSize: number;
67
67
  readonly codecSaturated: boolean;
68
+ readonly pendingFrames: number;
68
69
  configure(config: VideoDecoderConfig): void;
69
70
  decode(chunk: NativeEncodedVideoChunk): void;
70
71
  flush(): Promise<void>;
@@ -80,6 +81,7 @@ export interface NativeEncodedVideoChunk {
80
81
  readonly duration: number | null;
81
82
  readonly byteLength: number;
82
83
  copyTo(dest: Uint8Array | ArrayBuffer): void;
84
+ close(): void;
83
85
  }
84
86
  /**
85
87
  * Native AudioData object from C++ addon
@@ -111,6 +113,7 @@ export interface NativeEncodedAudioChunk {
111
113
  readonly duration: number | null;
112
114
  readonly byteLength: number;
113
115
  copyTo(dest: Uint8Array | ArrayBuffer): void;
116
+ close(): void;
114
117
  }
115
118
  /**
116
119
  * Native AudioEncoder object from C++ addon
@@ -151,6 +154,12 @@ export interface NativeVideoFilter {
151
154
  export interface NativeDemuxer {
152
155
  open(path: string): void;
153
156
  demux(): void;
157
+ /**
158
+ * Read packets from the file in chunks.
159
+ * @param maxPackets - Maximum number of packets to read. 0 = unlimited.
160
+ * @returns The number of packets actually read.
161
+ */
162
+ demuxPackets(maxPackets: number): number;
154
163
  close(): void;
155
164
  getVideoTrack(): TrackInfo | null;
156
165
  getAudioTrack(): TrackInfo | null;
@@ -462,6 +471,17 @@ export interface NativeModule {
462
471
  getFFmpegWarnings: () => string[];
463
472
  clearFFmpegWarnings: () => void;
464
473
  testAttrAsEnum: (obj: object, attr: string) => string;
474
+ getCounters: () => {
475
+ videoFrames: number;
476
+ audioData: number;
477
+ videoEncoders: number;
478
+ videoDecoders: number;
479
+ audioEncoders: number;
480
+ audioDecoders: number;
481
+ queue: number;
482
+ process: number;
483
+ frames: number;
484
+ };
465
485
  createEncoderConfigDescriptor: (config: object) => {
466
486
  codec: string;
467
487
  width: number;
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Get the runtime platform-architecture string.
3
- * Handles musl vs glibc distinction on Linux.
4
3
  */
5
4
  export declare function runtimePlatformArch(): string;
6
5
  /**
@@ -15,17 +14,9 @@ export declare function buildPlatformArch(): string;
15
14
  * Platforms with prebuilt binaries available.
16
15
  * Must match what release.yml actually builds.
17
16
  */
18
- export declare const prebuiltPlatforms: readonly ["darwin-arm64", "darwin-x64", "linux-x64", "linuxmusl-x64", "win32-x64"];
17
+ export declare const prebuiltPlatforms: readonly ["darwin-arm64", "darwin-x64", "linux-x64"];
19
18
  export type PrebuiltPlatform = (typeof prebuiltPlatforms)[number];
20
19
  /**
21
20
  * Check if a prebuilt binary is available for the current platform.
22
21
  */
23
22
  export declare function isPrebuiltAvailable(): boolean;
24
- /**
25
- * Get the npm package name for the prebuilt binary.
26
- */
27
- export declare function getPrebuiltPackageName(): string;
28
- /**
29
- * Get the npm package name for prebuilt FFmpeg.
30
- */
31
- export declare function getFFmpegPackageName(): string;
package/dist/platform.js CHANGED
@@ -41,36 +41,12 @@ exports.prebuiltPlatforms = void 0;
41
41
  exports.runtimePlatformArch = runtimePlatformArch;
42
42
  exports.buildPlatformArch = buildPlatformArch;
43
43
  exports.isPrebuiltAvailable = isPrebuiltAvailable;
44
- exports.getPrebuiltPackageName = getPrebuiltPackageName;
45
- exports.getFFmpegPackageName = getFFmpegPackageName;
46
44
  const os = __importStar(require("node:os"));
47
- // Try to detect musl vs glibc on Linux
48
- function detectLibc() {
49
- if (os.platform() !== 'linux')
50
- return null;
51
- try {
52
- const { familySync } = require('detect-libc');
53
- return familySync() === 'musl' ? 'musl' : 'glibc';
54
- }
55
- catch {
56
- // detect-libc not available, assume glibc
57
- return 'glibc';
58
- }
59
- }
60
45
  /**
61
46
  * Get the runtime platform-architecture string.
62
- * Handles musl vs glibc distinction on Linux.
63
47
  */
64
48
  function runtimePlatformArch() {
65
- const platform = os.platform();
66
- const arch = os.arch();
67
- if (platform === 'linux') {
68
- const libc = detectLibc();
69
- if (libc === 'musl') {
70
- return `linuxmusl-${arch}`;
71
- }
72
- }
73
- return `${platform}-${arch}`;
49
+ return `${os.platform()}-${os.arch()}`;
74
50
  }
75
51
  /**
76
52
  * Get the build-time platform-architecture string.
@@ -92,8 +68,6 @@ exports.prebuiltPlatforms = [
92
68
  'darwin-arm64',
93
69
  'darwin-x64',
94
70
  'linux-x64',
95
- 'linuxmusl-x64',
96
- 'win32-x64',
97
71
  ];
98
72
  /**
99
73
  * Check if a prebuilt binary is available for the current platform.
@@ -102,15 +76,3 @@ function isPrebuiltAvailable() {
102
76
  const platform = runtimePlatformArch();
103
77
  return exports.prebuiltPlatforms.includes(platform);
104
78
  }
105
- /**
106
- * Get the npm package name for the prebuilt binary.
107
- */
108
- function getPrebuiltPackageName() {
109
- return `@pproenca/node-webcodecs-${runtimePlatformArch()}`;
110
- }
111
- /**
112
- * Get the npm package name for prebuilt FFmpeg.
113
- */
114
- function getFFmpegPackageName() {
115
- return `@pproenca/ffmpeg-${runtimePlatformArch()}`;
116
- }
@@ -15,7 +15,6 @@ export declare class ResourceManager {
15
15
  private static instance;
16
16
  private codecs;
17
17
  private inactivityTimeout;
18
- private checkInterval;
19
18
  private constructor();
20
19
  static getInstance(): ResourceManager;
21
20
  /**
@@ -51,9 +50,9 @@ export declare class ResourceManager {
51
50
  * Set inactivity timeout (for testing).
52
51
  */
53
52
  setInactivityTimeout(ms: number): void;
54
- private startMonitoring;
55
53
  /**
56
54
  * Stop monitoring (for cleanup).
55
+ * @deprecated No longer needed - monitoring happens on-demand via getReclaimableCodecs()
57
56
  */
58
57
  stopMonitoring(): void;
59
58
  }
@@ -11,8 +11,7 @@ class ResourceManager {
11
11
  constructor() {
12
12
  this.codecs = new Map();
13
13
  this.inactivityTimeout = 10000; // 10 seconds per spec
14
- this.checkInterval = null;
15
- this.startMonitoring();
14
+ // Monitoring happens on-demand via getReclaimableCodecs()
16
15
  }
17
16
  static getInstance() {
18
17
  if (!ResourceManager.instance) {
@@ -104,25 +103,12 @@ class ResourceManager {
104
103
  setInactivityTimeout(ms) {
105
104
  this.inactivityTimeout = ms;
106
105
  }
107
- startMonitoring() {
108
- // Check every 5 seconds
109
- this.checkInterval = setInterval(() => {
110
- // Just track, don't auto-reclaim
111
- // Actual reclamation would be triggered by memory pressure
112
- }, 5000);
113
- // Don't keep process alive
114
- if (this.checkInterval.unref) {
115
- this.checkInterval.unref();
116
- }
117
- }
118
106
  /**
119
107
  * Stop monitoring (for cleanup).
108
+ * @deprecated No longer needed - monitoring happens on-demand via getReclaimableCodecs()
120
109
  */
121
110
  stopMonitoring() {
122
- if (this.checkInterval) {
123
- clearInterval(this.checkInterval);
124
- this.checkInterval = null;
125
- }
111
+ // No-op: monitoring now happens on-demand
126
112
  }
127
113
  }
128
114
  exports.ResourceManager = ResourceManager;