@doedja/scenecut 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +280 -0
  2. package/bin/cli.js +293 -0
  3. package/dist/decoder/ffmpeg-decoder.d.ts +50 -0
  4. package/dist/decoder/ffmpeg-decoder.d.ts.map +1 -0
  5. package/dist/decoder/ffmpeg-decoder.js +269 -0
  6. package/dist/decoder/ffmpeg-decoder.js.map +1 -0
  7. package/dist/decoder/frame-buffer.d.ts +81 -0
  8. package/dist/decoder/frame-buffer.d.ts.map +1 -0
  9. package/dist/decoder/frame-buffer.js +123 -0
  10. package/dist/decoder/frame-buffer.js.map +1 -0
  11. package/dist/detection/detector.d.ts +19 -0
  12. package/dist/detection/detector.d.ts.map +1 -0
  13. package/dist/detection/detector.js +126 -0
  14. package/dist/detection/detector.js.map +1 -0
  15. package/dist/detection/wasm-bridge.d.ts +82 -0
  16. package/dist/detection/wasm-bridge.d.ts.map +1 -0
  17. package/dist/detection/wasm-bridge.js +182 -0
  18. package/dist/detection/wasm-bridge.js.map +1 -0
  19. package/dist/detection.wasm.js +2 -0
  20. package/dist/detection.wasm.wasm +0 -0
  21. package/dist/index.d.ts +54 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +63 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/keyframes.cjs.js +985 -0
  26. package/dist/keyframes.cjs.js.map +1 -0
  27. package/dist/keyframes.esm.js +946 -0
  28. package/dist/keyframes.esm.js.map +1 -0
  29. package/dist/types/index.d.ts +225 -0
  30. package/dist/types/index.d.ts.map +1 -0
  31. package/dist/types/index.js +5 -0
  32. package/dist/types/index.js.map +1 -0
  33. package/dist/utils/buffer-pool.d.ts +44 -0
  34. package/dist/utils/buffer-pool.d.ts.map +1 -0
  35. package/dist/utils/buffer-pool.js +81 -0
  36. package/dist/utils/buffer-pool.js.map +1 -0
  37. package/dist/utils/frame-processor.d.ts +48 -0
  38. package/dist/utils/frame-processor.d.ts.map +1 -0
  39. package/dist/utils/frame-processor.js +112 -0
  40. package/dist/utils/frame-processor.js.map +1 -0
  41. package/package.json +77 -0
@@ -0,0 +1,269 @@
1
+ /**
2
+ * FFmpeg Decoder - Extract frames from video files
3
+ *
4
+ * Uses fluent-ffmpeg to extract grayscale frames for scene detection
5
+ */
6
+ import * as ffmpeg from 'fluent-ffmpeg';
7
+ import * as ffmpegInstaller from '@ffmpeg-installer/ffmpeg';
8
+ import { FrameBuffer } from './frame-buffer';
9
+ // Set FFmpeg path from installer
10
+ ffmpeg.setFfmpegPath(ffmpegInstaller.path);
11
+ /**
12
+ * Ring Buffer - Fixed-size circular buffer for streaming data
13
+ * Eliminates repeated Buffer.concat() allocations and GC pressure
14
+ */
15
+ class RingBuffer {
16
+ constructor(size = 8 * 1024 * 1024) {
17
+ this.writePos = 0;
18
+ this.readPos = 0;
19
+ this.availableBytes = 0;
20
+ this.buffer = Buffer.allocUnsafe(size);
21
+ this.capacity = size;
22
+ }
23
+ /**
24
+ * Write data to the ring buffer
25
+ */
26
+ write(chunk) {
27
+ const chunkSize = chunk.length;
28
+ if (chunkSize > this.capacity - this.availableBytes) {
29
+ throw new Error('RingBuffer overflow: chunk too large for available space');
30
+ }
31
+ // Write in two parts if wrapping around
32
+ const endSpace = this.capacity - this.writePos;
33
+ if (chunkSize <= endSpace) {
34
+ // No wrap-around needed
35
+ chunk.copy(this.buffer, this.writePos);
36
+ this.writePos += chunkSize;
37
+ }
38
+ else {
39
+ // Wrap-around: split write
40
+ chunk.copy(this.buffer, this.writePos, 0, endSpace);
41
+ chunk.copy(this.buffer, 0, endSpace, chunkSize);
42
+ this.writePos = chunkSize - endSpace;
43
+ }
44
+ // Wrap write position if at end
45
+ if (this.writePos >= this.capacity) {
46
+ this.writePos = 0;
47
+ }
48
+ this.availableBytes += chunkSize;
49
+ }
50
+ /**
51
+ * Read data from the ring buffer
52
+ */
53
+ read(size) {
54
+ if (size > this.availableBytes) {
55
+ throw new Error('RingBuffer underflow: not enough data available');
56
+ }
57
+ const result = Buffer.allocUnsafe(size);
58
+ const endSpace = this.capacity - this.readPos;
59
+ if (size <= endSpace) {
60
+ // No wrap-around needed
61
+ this.buffer.copy(result, 0, this.readPos, this.readPos + size);
62
+ this.readPos += size;
63
+ }
64
+ else {
65
+ // Wrap-around: split read
66
+ this.buffer.copy(result, 0, this.readPos, this.capacity);
67
+ this.buffer.copy(result, endSpace, 0, size - endSpace);
68
+ this.readPos = size - endSpace;
69
+ }
70
+ // Wrap read position if at end
71
+ if (this.readPos >= this.capacity) {
72
+ this.readPos = 0;
73
+ }
74
+ this.availableBytes -= size;
75
+ return result;
76
+ }
77
+ /**
78
+ * Get number of bytes available to read
79
+ */
80
+ available() {
81
+ return this.availableBytes;
82
+ }
83
+ /**
84
+ * Reset the ring buffer
85
+ */
86
+ reset() {
87
+ this.writePos = 0;
88
+ this.readPos = 0;
89
+ this.availableBytes = 0;
90
+ }
91
+ }
92
+ export class FFmpegDecoder {
93
+ constructor(videoPath, options = {}) {
94
+ this.metadata = null;
95
+ this.videoPath = videoPath;
96
+ this.options = {
97
+ pixelFormat: options.pixelFormat || 'gray',
98
+ maxBufferFrames: options.maxBufferFrames || 2,
99
+ skipFrames: options.skipFrames || 0
100
+ };
101
+ this.frameBuffer = new FrameBuffer(this.options.maxBufferFrames);
102
+ }
103
+ /**
104
+ * Get video metadata
105
+ */
106
+ async getMetadata() {
107
+ if (this.metadata) {
108
+ return this.metadata;
109
+ }
110
+ return new Promise((resolve, reject) => {
111
+ ffmpeg.ffprobe(this.videoPath, (err, metadata) => {
112
+ if (err) {
113
+ reject(new Error(`Failed to read video metadata: ${err.message}`));
114
+ return;
115
+ }
116
+ const videoStream = metadata.streams.find(s => s.codec_type === 'video');
117
+ if (!videoStream) {
118
+ reject(new Error('No video stream found'));
119
+ return;
120
+ }
121
+ const fps = this.parseFps(videoStream.r_frame_rate || videoStream.avg_frame_rate || '30/1');
122
+ const duration = parseFloat(String(metadata.format.duration || 0));
123
+ const totalFrames = Math.floor(duration * fps);
124
+ this.metadata = {
125
+ totalFrames,
126
+ duration,
127
+ fps,
128
+ resolution: {
129
+ width: videoStream.width || 0,
130
+ height: videoStream.height || 0
131
+ }
132
+ };
133
+ resolve(this.metadata);
134
+ });
135
+ });
136
+ }
137
+ /**
138
+ * Parse frame rate from FFmpeg format (e.g., "30000/1001")
139
+ */
140
+ parseFps(fpsString) {
141
+ const parts = fpsString.split('/');
142
+ if (parts.length === 2) {
143
+ return parseInt(parts[0]) / parseInt(parts[1]);
144
+ }
145
+ return parseFloat(fpsString);
146
+ }
147
+ /**
148
+ * Extract frames as grayscale data
149
+ *
150
+ * @param onFrame Callback for each frame
151
+ * @param onProgress Optional progress callback
152
+ */
153
+ async extractFrames(onFrame, onProgress) {
154
+ const metadata = await this.getMetadata();
155
+ const { width, height } = metadata.resolution;
156
+ return new Promise((resolve, reject) => {
157
+ let frameNumber = 0;
158
+ const ringBuffer = new RingBuffer(); // 8MB ring buffer
159
+ const frameSize = width * height; // Grayscale: 1 byte per pixel
160
+ const command = ffmpeg.default(this.videoPath)
161
+ .outputOptions([
162
+ '-f', 'image2pipe',
163
+ '-pix_fmt', 'gray',
164
+ '-vcodec', 'rawvideo'
165
+ ])
166
+ .on('error', (err) => {
167
+ reject(new Error(`FFmpeg error: ${err.message}`));
168
+ })
169
+ .on('end', () => {
170
+ resolve();
171
+ });
172
+ const stream = command.pipe();
173
+ stream.on('data', async (chunk) => {
174
+ // Write chunk to ring buffer (no allocation, no copying)
175
+ ringBuffer.write(chunk);
176
+ // Process complete frames
177
+ while (ringBuffer.available() >= frameSize) {
178
+ const frameData = ringBuffer.read(frameSize);
179
+ // Skip frames if requested
180
+ if (this.options.skipFrames > 0 && frameNumber % (this.options.skipFrames + 1) !== 0) {
181
+ frameNumber++;
182
+ continue;
183
+ }
184
+ // Create RawFrame
185
+ const frame = {
186
+ data: new Uint8Array(frameData),
187
+ width,
188
+ height,
189
+ stride: width,
190
+ pts: frameNumber / metadata.fps,
191
+ frameNumber
192
+ };
193
+ // Call callback
194
+ try {
195
+ await onFrame(frame);
196
+ }
197
+ catch (err) {
198
+ stream.destroy();
199
+ reject(err);
200
+ return;
201
+ }
202
+ // Progress callback
203
+ if (onProgress && frameNumber % 30 === 0) {
204
+ onProgress(frameNumber, metadata.totalFrames);
205
+ }
206
+ frameNumber++;
207
+ }
208
+ });
209
+ stream.on('error', (err) => {
210
+ reject(new Error(`Stream error: ${err.message}`));
211
+ });
212
+ });
213
+ }
214
+ /**
215
+ * Extract a single frame at specific frame number
216
+ */
217
+ async extractFrame(frameNumber) {
218
+ const metadata = await this.getMetadata();
219
+ const { width, height } = metadata.resolution;
220
+ const timestamp = frameNumber / metadata.fps;
221
+ return new Promise((resolve, reject) => {
222
+ const ringBuffer = new RingBuffer();
223
+ const frameSize = width * height;
224
+ const command = ffmpeg.default(this.videoPath)
225
+ .seekInput(timestamp)
226
+ .outputOptions([
227
+ '-vframes', '1',
228
+ '-f', 'image2pipe',
229
+ '-pix_fmt', 'gray',
230
+ '-vcodec', 'rawvideo'
231
+ ])
232
+ .on('error', (err) => {
233
+ reject(new Error(`FFmpeg error: ${err.message}`));
234
+ });
235
+ const stream = command.pipe();
236
+ stream.on('data', (chunk) => {
237
+ ringBuffer.write(chunk);
238
+ if (ringBuffer.available() >= frameSize) {
239
+ const frameData = ringBuffer.read(frameSize);
240
+ const frame = {
241
+ data: new Uint8Array(frameData),
242
+ width,
243
+ height,
244
+ stride: width,
245
+ pts: timestamp,
246
+ frameNumber
247
+ };
248
+ resolve(frame);
249
+ }
250
+ });
251
+ stream.on('error', (err) => {
252
+ reject(new Error(`Stream error: ${err.message}`));
253
+ });
254
+ });
255
+ }
256
+ /**
257
+ * Get the frame buffer
258
+ */
259
+ getFrameBuffer() {
260
+ return this.frameBuffer;
261
+ }
262
+ /**
263
+ * Clean up resources
264
+ */
265
+ destroy() {
266
+ this.frameBuffer.clear();
267
+ }
268
+ }
269
+ //# sourceMappingURL=ffmpeg-decoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffmpeg-decoder.js","sourceRoot":"","sources":["../../src/decoder/ffmpeg-decoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Frame Buffer - Circular buffer for frame management
3
+ *
4
+ * Maintains a circular buffer of frames for efficient scene detection
5
+ * Typically only needs to hold 2 frames (previous and current)
6
+ */
7
+ import { RawFrame } from '../types';
8
+ import { BufferPool } from '../utils/buffer-pool';
9
+ export declare class FrameBuffer {
10
+ private bufferPool;
11
+ private frames;
12
+ private maxFrames;
13
+ private currentIndex;
14
+ /**
15
+ * Create a new frame buffer
16
+ *
17
+ * @param maxFrames Maximum number of frames to buffer (default: 2)
18
+ * @param bufferPool Optional buffer pool for memory reuse
19
+ */
20
+ constructor(maxFrames?: number, bufferPool?: BufferPool);
21
+ /**
22
+ * Push a new frame into the buffer
23
+ *
24
+ * @param frame Frame to push
25
+ * @returns The frame that was evicted, if any
26
+ */
27
+ push(frame: RawFrame): RawFrame | null;
28
+ /**
29
+ * Get frame at specific offset from current position
30
+ *
31
+ * @param offset Offset from current position (0 = most recent, 1 = previous, etc.)
32
+ * @returns Frame or null if not available
33
+ */
34
+ get(offset?: number): RawFrame | null;
35
+ /**
36
+ * Get the most recent frame
37
+ */
38
+ getCurrent(): RawFrame | null;
39
+ /**
40
+ * Get the previous frame
41
+ */
42
+ getPrevious(): RawFrame | null;
43
+ /**
44
+ * Get both current and previous frames
45
+ *
46
+ * @returns [current, previous] or null if either is not available
47
+ */
48
+ getCurrentAndPrevious(): [RawFrame, RawFrame] | null;
49
+ /**
50
+ * Allocate a buffer for a new frame
51
+ *
52
+ * @param size Buffer size in bytes
53
+ * @returns Uint8Array buffer
54
+ */
55
+ allocateBuffer(size: number): Uint8Array;
56
+ /**
57
+ * Clear all frames from the buffer
58
+ */
59
+ clear(): void;
60
+ /**
61
+ * Get the number of frames currently in the buffer
62
+ */
63
+ size(): number;
64
+ /**
65
+ * Check if buffer is full
66
+ */
67
+ isFull(): boolean;
68
+ /**
69
+ * Get buffer statistics
70
+ */
71
+ getStats(): {
72
+ maxFrames: number;
73
+ currentFrames: number;
74
+ bufferPoolStats: {
75
+ size: number;
76
+ count: number;
77
+ }[];
78
+ totalPoolMemory: number;
79
+ };
80
+ }
81
+ //# sourceMappingURL=frame-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-buffer.d.ts","sourceRoot":"","sources":["../../src/decoder/frame-buffer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAa;IAEjC;;;;;OAKG;gBACS,SAAS,GAAE,MAAU,EAAE,UAAU,CAAC,EAAE,UAAU;IAM1D;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI;IAiBtC;;;;;OAKG;IACH,GAAG,CAAC,MAAM,GAAE,MAAU,GAAG,QAAQ,GAAG,IAAI;IASxC;;OAEG;IACH,UAAU,IAAI,QAAQ,GAAG,IAAI;IAI7B;;OAEG;IACH,WAAW,IAAI,QAAQ,GAAG,IAAI;IAI9B;;;;OAIG;IACH,qBAAqB,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,IAAI;IAWpD;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;IAIxC;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,QAAQ;;;;;;;;;CAQT"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Frame Buffer - Circular buffer for frame management
3
+ *
4
+ * Maintains a circular buffer of frames for efficient scene detection
5
+ * Typically only needs to hold 2 frames (previous and current)
6
+ */
7
+ import { BufferPool } from '../utils/buffer-pool';
8
+ export class FrameBuffer {
9
+ /**
10
+ * Create a new frame buffer
11
+ *
12
+ * @param maxFrames Maximum number of frames to buffer (default: 2)
13
+ * @param bufferPool Optional buffer pool for memory reuse
14
+ */
15
+ constructor(maxFrames = 2, bufferPool) {
16
+ this.currentIndex = 0;
17
+ this.maxFrames = maxFrames;
18
+ this.bufferPool = bufferPool || new BufferPool();
19
+ this.frames = new Array(maxFrames).fill(null);
20
+ }
21
+ /**
22
+ * Push a new frame into the buffer
23
+ *
24
+ * @param frame Frame to push
25
+ * @returns The frame that was evicted, if any
26
+ */
27
+ push(frame) {
28
+ const evictedFrame = this.frames[this.currentIndex];
29
+ // Release the evicted frame's buffer back to pool
30
+ if (evictedFrame) {
31
+ this.bufferPool.release(evictedFrame.data);
32
+ }
33
+ // Store the new frame
34
+ this.frames[this.currentIndex] = frame;
35
+ // Move to next position
36
+ this.currentIndex = (this.currentIndex + 1) % this.maxFrames;
37
+ return evictedFrame;
38
+ }
39
+ /**
40
+ * Get frame at specific offset from current position
41
+ *
42
+ * @param offset Offset from current position (0 = most recent, 1 = previous, etc.)
43
+ * @returns Frame or null if not available
44
+ */
45
+ get(offset = 0) {
46
+ if (offset < 0 || offset >= this.maxFrames) {
47
+ return null;
48
+ }
49
+ const index = (this.currentIndex - 1 - offset + this.maxFrames) % this.maxFrames;
50
+ return this.frames[index];
51
+ }
52
+ /**
53
+ * Get the most recent frame
54
+ */
55
+ getCurrent() {
56
+ return this.get(0);
57
+ }
58
+ /**
59
+ * Get the previous frame
60
+ */
61
+ getPrevious() {
62
+ return this.get(1);
63
+ }
64
+ /**
65
+ * Get both current and previous frames
66
+ *
67
+ * @returns [current, previous] or null if either is not available
68
+ */
69
+ getCurrentAndPrevious() {
70
+ const current = this.getCurrent();
71
+ const previous = this.getPrevious();
72
+ if (!current || !previous) {
73
+ return null;
74
+ }
75
+ return [current, previous];
76
+ }
77
+ /**
78
+ * Allocate a buffer for a new frame
79
+ *
80
+ * @param size Buffer size in bytes
81
+ * @returns Uint8Array buffer
82
+ */
83
+ allocateBuffer(size) {
84
+ return this.bufferPool.acquire(size);
85
+ }
86
+ /**
87
+ * Clear all frames from the buffer
88
+ */
89
+ clear() {
90
+ // Release all buffers back to pool
91
+ for (const frame of this.frames) {
92
+ if (frame) {
93
+ this.bufferPool.release(frame.data);
94
+ }
95
+ }
96
+ this.frames = new Array(this.maxFrames).fill(null);
97
+ this.currentIndex = 0;
98
+ }
99
+ /**
100
+ * Get the number of frames currently in the buffer
101
+ */
102
+ size() {
103
+ return this.frames.filter(f => f !== null).length;
104
+ }
105
+ /**
106
+ * Check if buffer is full
107
+ */
108
+ isFull() {
109
+ return this.size() === this.maxFrames;
110
+ }
111
+ /**
112
+ * Get buffer statistics
113
+ */
114
+ getStats() {
115
+ return {
116
+ maxFrames: this.maxFrames,
117
+ currentFrames: this.size(),
118
+ bufferPoolStats: this.bufferPool.getStats(),
119
+ totalPoolMemory: this.bufferPool.getTotalMemory()
120
+ };
121
+ }
122
+ }
123
+ //# sourceMappingURL=frame-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-buffer.js","sourceRoot":"","sources":["../../src/decoder/frame-buffer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Scene Detector - Main orchestrator for scene change detection
3
+ */
4
+ import { DetectionOptions, DetectionResult } from '../types';
5
+ export declare class SceneDetector {
6
+ private options;
7
+ private wasmBridge;
8
+ private state;
9
+ constructor(options?: DetectionOptions);
10
+ /**
11
+ * Detect scene changes in a video file
12
+ */
13
+ detect(videoPath: string): Promise<DetectionResult>;
14
+ /**
15
+ * Destroy the detector and clean up resources
16
+ */
17
+ destroy(): void;
18
+ }
19
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/detection/detector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EACL,gBAAgB,EAChB,eAAe,EAKhB,MAAM,UAAU,CAAC;AAOlB,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,KAAK,CAAiB;gBAElB,OAAO,GAAE,gBAAqB;IA0B1C;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAsHzD;;OAEG;IACH,OAAO,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Scene Detector - Main orchestrator for scene change detection
3
+ */
4
+ import { FFmpegDecoder } from '../decoder/ffmpeg-decoder';
5
+ import { WasmBridge } from './wasm-bridge';
6
+ import { formatTimecode, calculateFcode, validateFrame } from '../utils/frame-processor';
7
+ export class SceneDetector {
8
+ constructor(options = {}) {
9
+ // Set default options
10
+ this.options = {
11
+ sensitivity: options.sensitivity || 'medium',
12
+ customThresholds: options.customThresholds || { intraThresh: 2000, intraThresh2: 90 },
13
+ searchRange: options.searchRange || 'medium',
14
+ workers: options.workers || 1, // Multi-threading not implemented yet
15
+ progressive: options.progressive || { enabled: false, initialStep: 1, refinementSteps: [] },
16
+ temporalSmoothing: options.temporalSmoothing || { enabled: false, windowSize: 5, minConsecutive: 2 },
17
+ frameExtraction: options.frameExtraction || { pixelFormat: 'gray', maxBufferFrames: 2 },
18
+ onProgress: options.onProgress || (() => { }),
19
+ onScene: options.onScene || (() => { }),
20
+ format: options.format || 'json'
21
+ };
22
+ this.wasmBridge = new WasmBridge();
23
+ // Initialize detection state
24
+ this.state = {
25
+ intraCount: 1,
26
+ fcode: 4,
27
+ prevFrame: null,
28
+ curFrame: null
29
+ };
30
+ }
31
+ /**
32
+ * Detect scene changes in a video file
33
+ */
34
+ async detect(videoPath) {
35
+ // Initialize WASM module
36
+ await this.wasmBridge.init();
37
+ // Create decoder
38
+ const decoder = new FFmpegDecoder(videoPath, {
39
+ pixelFormat: this.options.frameExtraction.pixelFormat,
40
+ maxBufferFrames: this.options.frameExtraction.maxBufferFrames,
41
+ skipFrames: this.options.frameExtraction.skipFrames
42
+ });
43
+ // Get video metadata
44
+ const metadata = await decoder.getMetadata();
45
+ // Calculate fcode from search range
46
+ this.state.fcode = calculateFcode(this.options.searchRange, metadata.resolution.width, metadata.resolution.height);
47
+ // Pre-allocate WASM buffers (eliminates per-frame allocation overhead)
48
+ this.wasmBridge.allocateBuffers(metadata.resolution.width, metadata.resolution.height);
49
+ // Initialize scene list (frame 0 is always a scene change)
50
+ const scenes = [
51
+ {
52
+ frameNumber: 0,
53
+ timestamp: 0,
54
+ timecode: '00:00:00.000'
55
+ }
56
+ ];
57
+ // Processing statistics
58
+ const startTime = Date.now();
59
+ let processedFrames = 0;
60
+ // Process frames
61
+ await decoder.extractFrames(async (frame) => {
62
+ validateFrame(frame);
63
+ // Update current frame
64
+ this.state.curFrame = frame;
65
+ // Need at least 2 frames to detect scene change
66
+ if (this.state.prevFrame) {
67
+ const isSceneChange = this.wasmBridge.detectSceneChange(this.state.prevFrame, this.state.curFrame, this.state.intraCount, this.state.fcode);
68
+ if (isSceneChange) {
69
+ const scene = {
70
+ frameNumber: frame.frameNumber,
71
+ timestamp: frame.pts,
72
+ timecode: formatTimecode(frame.pts)
73
+ };
74
+ scenes.push(scene);
75
+ // Call scene callback
76
+ this.options.onScene(scene);
77
+ // Reset intraCount
78
+ this.state.intraCount = 1;
79
+ }
80
+ else {
81
+ // Increment intraCount
82
+ this.state.intraCount++;
83
+ }
84
+ }
85
+ // Move current frame to previous
86
+ this.state.prevFrame = this.state.curFrame;
87
+ processedFrames++;
88
+ }, (current, total) => {
89
+ // Progress callback
90
+ const progress = {
91
+ currentFrame: current,
92
+ totalFrames: total,
93
+ percent: Math.round((current / total) * 100)
94
+ };
95
+ // Calculate ETA
96
+ const elapsed = (Date.now() - startTime) / 1000;
97
+ const fps = current / elapsed;
98
+ const remaining = (total - current) / fps;
99
+ progress.eta = remaining;
100
+ this.options.onProgress(progress);
101
+ });
102
+ // Calculate statistics
103
+ const endTime = Date.now();
104
+ const processingTime = (endTime - startTime) / 1000;
105
+ const framesPerSecond = processedFrames / processingTime;
106
+ // Clean up
107
+ decoder.destroy();
108
+ return {
109
+ scenes,
110
+ metadata,
111
+ stats: {
112
+ processingTime,
113
+ framesPerSecond
114
+ }
115
+ };
116
+ }
117
+ /**
118
+ * Destroy the detector and clean up resources
119
+ */
120
+ destroy() {
121
+ this.wasmBridge.destroy();
122
+ this.state.prevFrame = null;
123
+ this.state.curFrame = null;
124
+ }
125
+ }
126
+ //# sourceMappingURL=detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/detection/detector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAS3C,OAAO,EACL,cAAc,EACd,cAAc,EACd,aAAa,EACd,MAAM,0BAA0B,CAAC;AAElC,MAAM,OAAO,aAAa;IAKxB,YAAY,UAA4B,EAAE;QACxC,sBAAsB;QACtB,IAAI,CAAC,OAAO,GAAG;YACb,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,QAAQ;YAC5C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE;YACrF,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,QAAQ;YAC5C,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,EAAE,sCAAsC;YACrE,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE;YAC3F,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;YACpG,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,EAAE;YACvF,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;YAC5C,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;YACtC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM;SACjC,CAAC;QAEF,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;QAEnC,6BAA6B;QAC7B,IAAI,CAAC,KAAK,GAAG;YACX,UAAU,EAAE,CAAC;YACb,KAAK,EAAE,CAAC;YACR,SAAS,EAAE,IAAI;YACf,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,yBAAyB;QACzB,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAE7B,iBAAiB;QACjB,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,SAAS,EAAE;YAC3C,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,WAAW;YACrD,eAAe,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,eAAe;YAC7D,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,UAAU;SACpD,CAAC,CAAC;QAEH,qBAAqB;QACrB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;QAE7C,oCAAoC;QACpC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,cAAc,CAC/B,IAAI,CAAC,OAAO,CAAC,WAAW,EACxB,QAAQ,CAAC,UAAU,CAAC,KAAK,EACzB,QAAQ,CAAC,UAAU,CAAC,MAAM,CAC3B,CAAC;QAEF,uEAAuE;QACvE,IAAI,CAAC,UAAU,CAAC,eAAe,CAC7B,QAAQ,CAAC,UAAU,CAAC,KAAK,EACzB,QAAQ,CAAC,UAAU,CAAC,MAAM,CAC3B,CAAC;QAEF,2DAA2D;QAC3D,MAAM,MAAM,GAAgB;YAC1B;gBACE,WAAW,EAAE,CAAC;gBACd,SAAS,EAAE,CAAC;gBACZ,QAAQ,EAAE,cAAc;aACzB;SACF,CAAC;QAEF,wBAAwB;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,eAAe,GAAG,CAAC,CAAC;QAExB,iBAAiB;QACjB,MAAM,OAAO,CAAC,aAAa,CACzB,KAAK,EAAE,KAAe,EAAE,EAAE;YACxB,aAAa,CAAC,KAAK,CAAC,CAAC;YAErB,uBAAuB;YACvB,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;YAE5B,gDAAgD;YAChD,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACzB,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,iBAAiB,CACrD,IAAI,CAAC,KAAK,CAAC,SAAS,EACpB,IAAI,CAAC,KAAK,CAAC,QAAQ,EACnB,IAAI,CAAC,KAAK,CAAC,UAAU,EACrB,IAAI,CAAC,KAAK,CAAC,KAAK,CACjB,CAAC;gBAEF,IAAI,aAAa,EAAE,CAAC;oBAClB,MAAM,KAAK,GAAc;wBACvB,WAAW,EAAE,KAAK,CAAC,WAAW;wBAC9B,SAAS,EAAE,KAAK,CAAC,GAAG;wBACpB,QAAQ,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC;qBACpC,CAAC;oBAEF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAEnB,sBAAsB;oBACtB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAE5B,mBAAmB;oBACnB,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;gBAC5B,CAAC;qBAAM,CAAC;oBACN,uBAAuB;oBACvB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;gBAC1B,CAAC;YACH,CAAC;YAED,iCAAiC;YACjC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;YAE3C,eAAe,EAAE,CAAC;QACpB,CAAC,EACD,CAAC,OAAe,EAAE,KAAa,EAAE,EAAE;YACjC,oBAAoB;YACpB,MAAM,QAAQ,GAAa;gBACzB,YAAY,EAAE,OAAO;gBACrB,WAAW,EAAE,KAAK;gBAClB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;aAC7C,CAAC;YAEF,gBAAgB;YAChB,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;YAChD,MAAM,GAAG,GAAG,OAAO,GAAG,OAAO,CAAC;YAC9B,MAAM,SAAS,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC;YAC1C,QAAQ,CAAC,GAAG,GAAG,SAAS,CAAC;YAEzB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC,CACF,CAAC;QAEF,uBAAuB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,cAAc,GAAG,CAAC,OAAO,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;QACpD,MAAM,eAAe,GAAG,eAAe,GAAG,cAAc,CAAC;QAEzD,WAAW;QACX,OAAO,CAAC,OAAO,EAAE,CAAC;QAElB,OAAO;YACL,MAAM;YACN,QAAQ;YACR,KAAK,EAAE;gBACL,cAAc;gBACd,eAAe;aAChB;SACF,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC7B,CAAC;CACF"}