@doedja/scenecut 1.0.0 → 1.0.2

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.
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Temporal Smoother - Sliding window filter to reduce false positives
3
+ *
4
+ * Three rules:
5
+ * 1. Minimum gap: Suppress detections within minConsecutive frames of each other (keep highest confidence)
6
+ * 2. Flash suppression: Isolated single-frame detections with low confidence are suppressed
7
+ * 3. Cluster merging: Consecutive triggered frames (common in dissolves) keep only highest-confidence one
8
+ */
9
+ export class TemporalSmoother {
10
+ constructor(config) {
11
+ // Sliding window of recent detections
12
+ this.recentDetections = [];
13
+ // Last confirmed scene change frame
14
+ this.lastConfirmedFrame = 0;
15
+ // Buffer for cluster detection
16
+ this.pendingCluster = [];
17
+ this.nonDetectionCount = 0;
18
+ // Flash suppression: minimum confidence for isolated detections
19
+ this.flashConfidenceThreshold = 0.4;
20
+ this.windowSize = config.windowSize;
21
+ this.minConsecutive = config.minConsecutive;
22
+ }
23
+ /**
24
+ * Process a frame's detection result through temporal smoothing
25
+ */
26
+ process(frameNumber, rawIsSceneChange, rawConfidence) {
27
+ // If no detection, track gap and possibly flush pending cluster
28
+ if (!rawIsSceneChange) {
29
+ this.nonDetectionCount++;
30
+ // If we had a pending cluster and enough non-detections have passed,
31
+ // emit the best detection from the cluster
32
+ if (this.pendingCluster.length > 0 && this.nonDetectionCount >= 2) {
33
+ const best = this.flushCluster();
34
+ if (best) {
35
+ return best;
36
+ }
37
+ }
38
+ return { isSceneChange: false, confidence: 0 };
39
+ }
40
+ // We have a detection
41
+ this.nonDetectionCount = 0;
42
+ // Rule 1: Minimum gap enforcement
43
+ if (frameNumber - this.lastConfirmedFrame < this.minConsecutive) {
44
+ // Too close to last confirmed scene change
45
+ // If this has higher confidence, replace pending, but don't emit yet
46
+ if (this.pendingCluster.length > 0) {
47
+ const best = this.pendingCluster.reduce((a, b) => a.confidence > b.confidence ? a : b);
48
+ if (rawConfidence > best.confidence) {
49
+ // Replace entire cluster with this better detection
50
+ this.pendingCluster = [{ frameNumber, confidence: rawConfidence }];
51
+ }
52
+ }
53
+ return { isSceneChange: false, confidence: 0 };
54
+ }
55
+ // Rule 3: Cluster merging - add to pending cluster
56
+ this.pendingCluster.push({ frameNumber, confidence: rawConfidence });
57
+ // Don't emit immediately; wait to see if more consecutive detections follow
58
+ return { isSceneChange: false, confidence: 0 };
59
+ }
60
+ /**
61
+ * Flush the pending cluster, emitting the highest-confidence detection
62
+ */
63
+ flushCluster() {
64
+ if (this.pendingCluster.length === 0) {
65
+ return null;
66
+ }
67
+ // Find the detection with highest confidence
68
+ const best = this.pendingCluster.reduce((a, b) => a.confidence > b.confidence ? a : b);
69
+ // Rule 2: Flash suppression - isolated single-frame detections with low confidence
70
+ if (this.pendingCluster.length === 1 && best.confidence < this.flashConfidenceThreshold) {
71
+ this.pendingCluster = [];
72
+ return null;
73
+ }
74
+ // Confirm this detection
75
+ this.lastConfirmedFrame = best.frameNumber;
76
+ this.recentDetections.push(best);
77
+ // Keep sliding window bounded
78
+ while (this.recentDetections.length > this.windowSize) {
79
+ this.recentDetections.shift();
80
+ }
81
+ this.pendingCluster = [];
82
+ return {
83
+ isSceneChange: true,
84
+ confidence: best.confidence
85
+ };
86
+ }
87
+ }
88
+ //# sourceMappingURL=temporal-smoother.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temporal-smoother.js","sourceRoot":"","sources":["../../src/detection/temporal-smoother.ts"],"names":[],"mappings":"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"}
@@ -6,15 +6,22 @@
6
6
  * - Memory allocation and management
7
7
  * - Calling WASM functions
8
8
  * - Data marshalling between JS and WASM
9
+ * - Double-buffering to avoid redundant frame copies
9
10
  */
10
11
  import { RawFrame } from '../types';
12
+ export interface SceneChangeResult {
13
+ isSceneChange: boolean;
14
+ confidence: number;
15
+ }
11
16
  export declare class WasmBridge {
12
17
  private module;
13
18
  private initialized;
14
- private prevFramePtr;
15
- private curFramePtr;
16
- private prevPaddedPtr;
17
- private curPaddedPtr;
19
+ private slotARawPtr;
20
+ private slotBRawPtr;
21
+ private slotAPaddedPtr;
22
+ private slotBPaddedPtr;
23
+ private prevIsSlotA;
24
+ private prevSlotPadded;
18
25
  private allocatedFrameSize;
19
26
  private allocatedPaddedSize;
20
27
  /**
@@ -26,40 +33,36 @@ export declare class WasmBridge {
26
33
  */
27
34
  private ensureInitialized;
28
35
  /**
29
- * Pre-allocate WASM buffers for frame processing
30
- * This eliminates per-frame allocation overhead and reduces memory copies
31
- *
32
- * @param width Frame width
33
- * @param height Frame height
36
+ * Pre-allocate WASM buffers for frame processing.
37
+ * Allocates double-buffered raw + padded slots and pre-allocates the MB array.
34
38
  */
35
39
  allocateBuffers(width: number, height: number): void;
36
40
  /**
37
- * Detect scene change between two frames
41
+ * Detect scene change between two frames using double-buffering.
38
42
  *
39
- * Uses pre-allocated WASM buffers to eliminate per-frame allocation
40
- * and reduce memory copies from 3 to 1 per frame.
43
+ * On first call, both frames are copied and padded.
44
+ * On subsequent calls, only the new current frame is copied and padded;
45
+ * the previous frame is already in WASM memory from the last call.
41
46
  *
42
47
  * @param prevFrame Previous frame
43
48
  * @param curFrame Current frame
44
49
  * @param intraCount Number of consecutive non-scene-change frames
45
- * @param fcode Motion search range parameter (default: 4 = 256 pixels)
46
- * @returns true if scene change detected, false otherwise
50
+ * @param fcode Motion search range parameter
51
+ * @param intraThresh Primary intra threshold
52
+ * @param intraThresh2 Secondary intra threshold (sSAD comparison)
53
+ * @returns Scene change result with confidence score
54
+ */
55
+ detectSceneChange(prevFrame: RawFrame, curFrame: RawFrame, intraCount: number, fcode?: number, intraThresh?: number, intraThresh2?: number): SceneChangeResult;
56
+ /**
57
+ * Reset double-buffer state (e.g., after a seek or when starting fresh)
47
58
  */
48
- detectSceneChange(prevFrame: RawFrame, curFrame: RawFrame, intraCount: number, fcode?: number): boolean;
59
+ resetBufferState(): void;
49
60
  /**
50
61
  * Calculate required buffer size for a padded frame
51
- *
52
- * @param width Original frame width
53
- * @param height Original frame height
54
- * @returns Required buffer size in bytes
55
62
  */
56
63
  calculatePaddedSize(width: number, height: number): number;
57
64
  /**
58
65
  * Get macroblock parameters for a given frame size
59
- *
60
- * @param width Frame width
61
- * @param height Frame height
62
- * @returns Macroblock parameters
63
66
  */
64
67
  getMBParam(width: number, height: number): {
65
68
  width: number;
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-bridge.d.ts","sourceRoot":"","sources":["../../src/detection/wasm-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAc,QAAQ,EAAE,MAAM,UAAU,CAAC;AAIhD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,WAAW,CAAkB;IAGrC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,mBAAmB,CAAa;IAExC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;;;;;OAMG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IA2BpD;;;;;;;;;;;OAWG;IACH,iBAAiB,CACf,SAAS,EAAE,QAAQ,EACnB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,MAAU,GAChB,OAAO;IAkCV;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAK1D;;;;;;OAMG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAgBxC;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,OAAO,IAAI,IAAI;CAmBhB"}
1
+ {"version":3,"file":"wasm-bridge.d.ts","sourceRoot":"","sources":["../../src/detection/wasm-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAc,QAAQ,EAAE,MAAM,UAAU,CAAC;AAIhD,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,WAAW,CAAkB;IAIrC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAEhC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;IAEnC,OAAO,CAAC,WAAW,CAAiB;IAEpC,OAAO,CAAC,cAAc,CAAkB;IAExC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,mBAAmB,CAAa;IAIxC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAqCpD;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CACf,SAAS,EAAE,QAAQ,EACnB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,MAAU,EACjB,WAAW,GAAE,MAAa,EAC1B,YAAY,GAAE,MAAW,GACxB,iBAAiB;IAqEpB;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAKxB;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAK1D;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAgBxC;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,OAAO,IAAI,IAAI;CAuBhB"}
@@ -6,6 +6,7 @@
6
6
  * - Memory allocation and management
7
7
  * - Calling WASM functions
8
8
  * - Data marshalling between JS and WASM
9
+ * - Double-buffering to avoid redundant frame copies
9
10
  */
10
11
  import * as path from 'path';
11
12
  import * as fs from 'fs';
@@ -13,14 +14,21 @@ export class WasmBridge {
13
14
  constructor() {
14
15
  this.module = null;
15
16
  this.initialized = false;
16
- // Pre-allocated WASM buffers for frame processing
17
- this.prevFramePtr = 0; // Raw previous frame
18
- this.curFramePtr = 0; // Raw current frame
19
- this.prevPaddedPtr = 0; // Padded previous frame
20
- this.curPaddedPtr = 0; // Padded current frame
21
- this.allocatedFrameSize = 0; // Size of raw frame buffers
22
- this.allocatedPaddedSize = 0; // Size of padded frame buffers
17
+ // Double-buffered WASM pointers for frame processing
18
+ // Slot A and Slot B raw frame buffers
19
+ this.slotARawPtr = 0;
20
+ this.slotBRawPtr = 0;
21
+ // Slot A and Slot B padded frame buffers
22
+ this.slotAPaddedPtr = 0;
23
+ this.slotBPaddedPtr = 0;
24
+ // Which slot currently holds the "previous" frame (true = A, false = B)
25
+ this.prevIsSlotA = true;
26
+ // Whether the previous slot has valid padded data
27
+ this.prevSlotPadded = false;
28
+ this.allocatedFrameSize = 0;
29
+ this.allocatedPaddedSize = 0;
23
30
  }
31
+ // Frame dimensions (reserved for future use in validation/resizing)
24
32
  /**
25
33
  * Initialize the WASM module
26
34
  */
@@ -53,11 +61,8 @@ export class WasmBridge {
53
61
  }
54
62
  }
55
63
  /**
56
- * Pre-allocate WASM buffers for frame processing
57
- * This eliminates per-frame allocation overhead and reduces memory copies
58
- *
59
- * @param width Frame width
60
- * @param height Frame height
64
+ * Pre-allocate WASM buffers for frame processing.
65
+ * Allocates double-buffered raw + padded slots and pre-allocates the MB array.
61
66
  */
62
67
  allocateBuffers(width, height) {
63
68
  this.ensureInitialized();
@@ -65,63 +70,104 @@ export class WasmBridge {
65
70
  const paddedSize = this.module._calculate_padded_size(width, height);
66
71
  // Allocate or re-allocate raw frame buffers if size changed
67
72
  if (frameSize !== this.allocatedFrameSize) {
68
- if (this.prevFramePtr)
69
- this.module._free(this.prevFramePtr);
70
- if (this.curFramePtr)
71
- this.module._free(this.curFramePtr);
72
- this.prevFramePtr = this.module._malloc(frameSize);
73
- this.curFramePtr = this.module._malloc(frameSize);
73
+ if (this.slotARawPtr)
74
+ this.module._free(this.slotARawPtr);
75
+ if (this.slotBRawPtr)
76
+ this.module._free(this.slotBRawPtr);
77
+ this.slotARawPtr = this.module._malloc(frameSize);
78
+ this.slotBRawPtr = this.module._malloc(frameSize);
74
79
  this.allocatedFrameSize = frameSize;
75
80
  }
76
81
  // Allocate or re-allocate padded frame buffers if size changed
77
82
  if (paddedSize !== this.allocatedPaddedSize) {
78
- if (this.prevPaddedPtr)
79
- this.module._free(this.prevPaddedPtr);
80
- if (this.curPaddedPtr)
81
- this.module._free(this.curPaddedPtr);
82
- this.prevPaddedPtr = this.module._malloc(paddedSize);
83
- this.curPaddedPtr = this.module._malloc(paddedSize);
83
+ if (this.slotAPaddedPtr)
84
+ this.module._free(this.slotAPaddedPtr);
85
+ if (this.slotBPaddedPtr)
86
+ this.module._free(this.slotBPaddedPtr);
87
+ this.slotAPaddedPtr = this.module._malloc(paddedSize);
88
+ this.slotBPaddedPtr = this.module._malloc(paddedSize);
84
89
  this.allocatedPaddedSize = paddedSize;
85
90
  }
91
+ // Reset double-buffer state
92
+ this.prevIsSlotA = true;
93
+ this.prevSlotPadded = false;
94
+ // Pre-allocate macroblock array in WASM
95
+ const mbResult = this.module._allocate_mb_array(width, height);
96
+ if (mbResult === 0) {
97
+ throw new Error('Failed to pre-allocate macroblock array in WASM');
98
+ }
86
99
  }
87
100
  /**
88
- * Detect scene change between two frames
101
+ * Detect scene change between two frames using double-buffering.
89
102
  *
90
- * Uses pre-allocated WASM buffers to eliminate per-frame allocation
91
- * and reduce memory copies from 3 to 1 per frame.
103
+ * On first call, both frames are copied and padded.
104
+ * On subsequent calls, only the new current frame is copied and padded;
105
+ * the previous frame is already in WASM memory from the last call.
92
106
  *
93
107
  * @param prevFrame Previous frame
94
108
  * @param curFrame Current frame
95
109
  * @param intraCount Number of consecutive non-scene-change frames
96
- * @param fcode Motion search range parameter (default: 4 = 256 pixels)
97
- * @returns true if scene change detected, false otherwise
110
+ * @param fcode Motion search range parameter
111
+ * @param intraThresh Primary intra threshold
112
+ * @param intraThresh2 Secondary intra threshold (sSAD comparison)
113
+ * @returns Scene change result with confidence score
98
114
  */
99
- detectSceneChange(prevFrame, curFrame, intraCount, fcode = 4) {
115
+ detectSceneChange(prevFrame, curFrame, intraCount, fcode = 4, intraThresh = 2000, intraThresh2 = 90) {
100
116
  this.ensureInitialized();
101
117
  // Validate inputs
102
118
  if (prevFrame.width !== curFrame.width || prevFrame.height !== curFrame.height) {
103
119
  throw new Error('Frame dimensions must match');
104
120
  }
105
- // Ensure buffers are allocated (should be done once at start)
106
- if (!this.prevFramePtr || this.allocatedFrameSize !== prevFrame.data.length) {
121
+ // Ensure buffers are allocated
122
+ if (!this.slotARawPtr || this.allocatedFrameSize !== prevFrame.data.length) {
107
123
  this.allocateBuffers(prevFrame.width, prevFrame.height);
108
124
  }
109
- // Single copy: Raw frames -> WASM memory (eliminates 2 extra copies)
110
- this.module.HEAPU8.set(prevFrame.data, this.prevFramePtr);
111
- this.module.HEAPU8.set(curFrame.data, this.curFramePtr);
112
- // Pad frames in-place in WASM (no copy back to JS)
113
- this.module._pad_frame(this.prevFramePtr, this.prevPaddedPtr, prevFrame.width, prevFrame.height);
114
- this.module._pad_frame(this.curFramePtr, this.curPaddedPtr, curFrame.width, curFrame.height);
115
- // Run motion estimation on pre-padded buffers
116
- const result = this.module._MEanalysis_js(this.prevPaddedPtr, this.curPaddedPtr, prevFrame.width, prevFrame.height, intraCount, fcode);
117
- return result === 1;
125
+ // Determine which slot is "prev" and which is "cur"
126
+ const prevRawPtr = this.prevIsSlotA ? this.slotARawPtr : this.slotBRawPtr;
127
+ const prevPaddedPtr = this.prevIsSlotA ? this.slotAPaddedPtr : this.slotBPaddedPtr;
128
+ const curRawPtr = this.prevIsSlotA ? this.slotBRawPtr : this.slotARawPtr;
129
+ const curPaddedPtr = this.prevIsSlotA ? this.slotBPaddedPtr : this.slotAPaddedPtr;
130
+ // Copy and pad previous frame only if not already valid in WASM
131
+ if (!this.prevSlotPadded) {
132
+ this.module.HEAPU8.set(prevFrame.data, prevRawPtr);
133
+ this.module._pad_frame(prevRawPtr, prevPaddedPtr, prevFrame.width, prevFrame.height);
134
+ }
135
+ // Always copy and pad the new current frame
136
+ this.module.HEAPU8.set(curFrame.data, curRawPtr);
137
+ this.module._pad_frame(curRawPtr, curPaddedPtr, curFrame.width, curFrame.height);
138
+ // Run motion estimation with parameterized thresholds
139
+ const rawScore = this.module._MEanalysis_js(prevPaddedPtr, curPaddedPtr, prevFrame.width, prevFrame.height, intraCount, fcode, intraThresh, intraThresh2);
140
+ // Check for WASM error
141
+ if (rawScore === -1) {
142
+ throw new Error('WASM memory allocation failed during scene detection. ' +
143
+ `Frame size: ${prevFrame.width}x${prevFrame.height}. ` +
144
+ 'The video resolution may be too high for available WASM memory.');
145
+ }
146
+ // Swap roles: current slot becomes previous for next call
147
+ this.prevIsSlotA = !this.prevIsSlotA;
148
+ this.prevSlotPadded = true;
149
+ // Determine scene change and confidence
150
+ const isSceneChange = rawScore >= intraThresh2;
151
+ // Normalize confidence: 0 when at threshold, 1 at 2x threshold
152
+ // For non-scene-changes, confidence represents "how close" (0 = very far from threshold)
153
+ let confidence;
154
+ if (isSceneChange) {
155
+ confidence = Math.min(1.0, rawScore / (intraThresh2 * 2));
156
+ }
157
+ else {
158
+ confidence = intraThresh2 > 0 ? Math.min(1.0, rawScore / intraThresh2) : 0;
159
+ }
160
+ return { isSceneChange, confidence };
161
+ }
162
+ /**
163
+ * Reset double-buffer state (e.g., after a seek or when starting fresh)
164
+ */
165
+ resetBufferState() {
166
+ this.prevIsSlotA = true;
167
+ this.prevSlotPadded = false;
118
168
  }
119
169
  /**
120
170
  * Calculate required buffer size for a padded frame
121
- *
122
- * @param width Original frame width
123
- * @param height Original frame height
124
- * @returns Required buffer size in bytes
125
171
  */
126
172
  calculatePaddedSize(width, height) {
127
173
  this.ensureInitialized();
@@ -129,10 +175,6 @@ export class WasmBridge {
129
175
  }
130
176
  /**
131
177
  * Get macroblock parameters for a given frame size
132
- *
133
- * @param width Frame width
134
- * @param height Frame height
135
- * @returns Macroblock parameters
136
178
  */
137
179
  getMBParam(width, height) {
138
180
  const mb_width = Math.ceil(width / 16);
@@ -158,23 +200,26 @@ export class WasmBridge {
158
200
  * Clean up resources
159
201
  */
160
202
  destroy() {
161
- // Free pre-allocated WASM buffers
162
203
  if (this.module) {
163
- if (this.prevFramePtr)
164
- this.module._free(this.prevFramePtr);
165
- if (this.curFramePtr)
166
- this.module._free(this.curFramePtr);
167
- if (this.prevPaddedPtr)
168
- this.module._free(this.prevPaddedPtr);
169
- if (this.curPaddedPtr)
170
- this.module._free(this.curPaddedPtr);
204
+ // Free pre-allocated macroblock array
205
+ this.module._free_mb_array();
206
+ // Free double-buffered WASM frame buffers
207
+ if (this.slotARawPtr)
208
+ this.module._free(this.slotARawPtr);
209
+ if (this.slotBRawPtr)
210
+ this.module._free(this.slotBRawPtr);
211
+ if (this.slotAPaddedPtr)
212
+ this.module._free(this.slotAPaddedPtr);
213
+ if (this.slotBPaddedPtr)
214
+ this.module._free(this.slotBPaddedPtr);
171
215
  }
172
- this.prevFramePtr = 0;
173
- this.curFramePtr = 0;
174
- this.prevPaddedPtr = 0;
175
- this.curPaddedPtr = 0;
216
+ this.slotARawPtr = 0;
217
+ this.slotBRawPtr = 0;
218
+ this.slotAPaddedPtr = 0;
219
+ this.slotBPaddedPtr = 0;
176
220
  this.allocatedFrameSize = 0;
177
221
  this.allocatedPaddedSize = 0;
222
+ this.prevSlotPadded = false;
178
223
  this.module = null;
179
224
  this.initialized = false;
180
225
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-bridge.js","sourceRoot":"","sources":["../../src/detection/wasm-bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wasm-bridge.js","sourceRoot":"","sources":["../../src/detection/wasm-bridge.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var createWasmModule=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["c"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("detection.wasm.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["getValue"]=getValue;var _MEanalysis_js,_free,_calculate_padded_size,_pad_frame,_malloc,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_MEanalysis_js=Module["_MEanalysis_js"]=wasmExports["d"];_free=Module["_free"]=wasmExports["e"];_calculate_padded_size=Module["_calculate_padded_size"]=wasmExports["f"];_pad_frame=Module["_pad_frame"]=wasmExports["g"];_malloc=Module["_malloc"]=wasmExports["h"];__emscripten_stack_restore=wasmExports["i"];__emscripten_stack_alloc=wasmExports["j"];_emscripten_stack_get_current=wasmExports["k"];memory=wasmMemory=wasmExports["b"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={a:_emscripten_resize_heap};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
2
- ;return moduleRtn}})();if(typeof exports==="object"&&typeof module==="object"){module.exports=createWasmModule;module.exports.default=createWasmModule}else if(typeof define==="function"&&define["amd"])define([],()=>createWasmModule);
1
+ var createWasmModule=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["c"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("detection.wasm.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["getValue"]=getValue;var _allocate_mb_array,_free,_malloc,_free_mb_array,_MEanalysis_js,_calculate_padded_size,_pad_frame,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory;function assignWasmExports(wasmExports){_allocate_mb_array=Module["_allocate_mb_array"]=wasmExports["d"];_free=Module["_free"]=wasmExports["e"];_malloc=Module["_malloc"]=wasmExports["f"];_free_mb_array=Module["_free_mb_array"]=wasmExports["g"];_MEanalysis_js=Module["_MEanalysis_js"]=wasmExports["h"];_calculate_padded_size=Module["_calculate_padded_size"]=wasmExports["i"];_pad_frame=Module["_pad_frame"]=wasmExports["j"];__emscripten_stack_restore=wasmExports["k"];__emscripten_stack_alloc=wasmExports["l"];_emscripten_stack_get_current=wasmExports["m"];memory=wasmMemory=wasmExports["b"];__indirect_function_table=wasmExports["__indirect_function_table"]}var wasmImports={a:_emscripten_resize_heap};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
2
+ ;return moduleRtn}})();if(typeof exports==="object"&&typeof module==="object"){module.exports=createWasmModule;module.exports.default=createWasmModule}else if(typeof define==="function"&&define["amd"])define([],()=>createWasmModule);
Binary file
package/dist/index.d.ts CHANGED
@@ -7,11 +7,12 @@
7
7
  export { SceneDetector } from './detection/detector';
8
8
  export { FFmpegDecoder } from './decoder/ffmpeg-decoder';
9
9
  export { WasmBridge } from './detection/wasm-bridge';
10
+ export { TemporalSmoother } from './detection/temporal-smoother';
10
11
  export { FrameBuffer } from './decoder/frame-buffer';
11
12
  export { BufferPool } from './utils/buffer-pool';
12
- export type { DetectionOptions, DetectionResult, SceneInfo, VideoMetadata, DetectionStats, Progress, RawFrame, SensitivityLevel, SearchRange, CustomThresholds, TemporalSmoothing, ProgressiveProcessing, FrameExtractionOptions } from './types';
13
+ export type { DetectionOptions, DetectionResult, SceneInfo, VideoMetadata, DetectionStats, Progress, RawFrame, SensitivityLevel, SearchRange, CustomThresholds, TemporalSmoothing, ProgressiveProcessing, FrameExtractionOptions, FrameImageOptions } from './types';
13
14
  export { formatTimecode, calculateFcode, calculateThresholds, validateFrame, validateFrameDimensions, calculateMBParam, calculateFrameMemory, estimateProcessingTime } from './utils/frame-processor';
14
- import { DetectionOptions, DetectionResult } from './types';
15
+ import { DetectionOptions, DetectionResult, FrameImageOptions } from './types';
15
16
  /**
16
17
  * Detect scene changes in a video file (simple API)
17
18
  *
@@ -27,11 +28,20 @@ import { DetectionOptions, DetectionResult } from './types';
27
28
  * console.log(`Found ${results.scenes.length} scenes`);
28
29
  *
29
30
  * results.scenes.forEach(scene => {
30
- * console.log(`Scene at ${scene.timecode}`);
31
+ * console.log(`Scene at ${scene.timecode} (confidence: ${scene.confidence})`);
31
32
  * });
32
33
  * ```
33
34
  */
34
35
  export declare function detectSceneChanges(videoPath: string, options?: DetectionOptions): Promise<DetectionResult>;
36
+ /**
37
+ * Extract scene thumbnail images from a video
38
+ *
39
+ * @param videoPath Path to video file
40
+ * @param options Detection options
41
+ * @param imageOptions Image extraction options
42
+ * @returns Detection results (images are written to disk)
43
+ */
44
+ export declare function extractSceneImages(videoPath: string, options?: DetectionOptions, imageOptions?: FrameImageOptions): Promise<DetectionResult>;
35
45
  /**
36
46
  * Version information
37
47
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,aAAa,EACb,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC,CAS1B;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;CAWhB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,aAAa,EACb,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE/E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC,CAS1B;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,gBAAgB,EAC1B,YAAY,CAAC,EAAE,iBAAiB,GAC/B,OAAO,CAAC,eAAe,CAAC,CAiB1B;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;CAWhB,CAAC"}
package/dist/index.js CHANGED
@@ -7,11 +7,13 @@
7
7
  export { SceneDetector } from './detection/detector';
8
8
  export { FFmpegDecoder } from './decoder/ffmpeg-decoder';
9
9
  export { WasmBridge } from './detection/wasm-bridge';
10
+ export { TemporalSmoother } from './detection/temporal-smoother';
10
11
  export { FrameBuffer } from './decoder/frame-buffer';
11
12
  export { BufferPool } from './utils/buffer-pool';
12
13
  // Export utilities
13
14
  export { formatTimecode, calculateFcode, calculateThresholds, validateFrame, validateFrameDimensions, calculateMBParam, calculateFrameMemory, estimateProcessingTime } from './utils/frame-processor';
14
15
  import { SceneDetector } from './detection/detector';
16
+ import { FFmpegDecoder } from './decoder/ffmpeg-decoder';
15
17
  /**
16
18
  * Detect scene changes in a video file (simple API)
17
19
  *
@@ -27,7 +29,7 @@ import { SceneDetector } from './detection/detector';
27
29
  * console.log(`Found ${results.scenes.length} scenes`);
28
30
  *
29
31
  * results.scenes.forEach(scene => {
30
- * console.log(`Scene at ${scene.timecode}`);
32
+ * console.log(`Scene at ${scene.timecode} (confidence: ${scene.confidence})`);
31
33
  * });
32
34
  * ```
33
35
  */
@@ -41,6 +43,30 @@ export async function detectSceneChanges(videoPath, options) {
41
43
  detector.destroy();
42
44
  }
43
45
  }
46
+ /**
47
+ * Extract scene thumbnail images from a video
48
+ *
49
+ * @param videoPath Path to video file
50
+ * @param options Detection options
51
+ * @param imageOptions Image extraction options
52
+ * @returns Detection results (images are written to disk)
53
+ */
54
+ export async function extractSceneImages(videoPath, options, imageOptions) {
55
+ const detector = new SceneDetector(options);
56
+ try {
57
+ const results = await detector.detect(videoPath);
58
+ if (imageOptions) {
59
+ const decoder = new FFmpegDecoder(videoPath);
60
+ const frameNumbers = results.scenes.map(s => s.frameNumber);
61
+ await decoder.extractFrameImages(frameNumbers, imageOptions);
62
+ decoder.destroy();
63
+ }
64
+ return results;
65
+ }
66
+ finally {
67
+ detector.destroy();
68
+ }
69
+ }
44
70
  /**
45
71
  * Version information
46
72
  */