vidpipe 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.
- package/README.md +243 -0
- package/assets/fonts/Montserrat-Bold.ttf +0 -0
- package/assets/fonts/Montserrat-Regular.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/dist/__tests__/agents.test.d.ts +2 -0
- package/dist/__tests__/agents.test.d.ts.map +1 -0
- package/dist/__tests__/agents.test.js +434 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/aspectRatio.test.d.ts +2 -0
- package/dist/__tests__/aspectRatio.test.d.ts.map +1 -0
- package/dist/__tests__/aspectRatio.test.js +406 -0
- package/dist/__tests__/aspectRatio.test.js.map +1 -0
- package/dist/__tests__/captionGenerator.test.d.ts +2 -0
- package/dist/__tests__/captionGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/captionGenerator.test.js +435 -0
- package/dist/__tests__/captionGenerator.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +81 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/faceDetection.test.d.ts +2 -0
- package/dist/__tests__/faceDetection.test.d.ts.map +1 -0
- package/dist/__tests__/faceDetection.test.js +372 -0
- package/dist/__tests__/faceDetection.test.js.map +1 -0
- package/dist/__tests__/ffmpegTools.test.d.ts +2 -0
- package/dist/__tests__/ffmpegTools.test.d.ts.map +1 -0
- package/dist/__tests__/ffmpegTools.test.js +464 -0
- package/dist/__tests__/ffmpegTools.test.js.map +1 -0
- package/dist/__tests__/integration/captionBurn.test.d.ts +2 -0
- package/dist/__tests__/integration/captionBurn.test.d.ts.map +1 -0
- package/dist/__tests__/integration/captionBurn.test.js +103 -0
- package/dist/__tests__/integration/captionBurn.test.js.map +1 -0
- package/dist/__tests__/integration/clipComposite.test.d.ts +2 -0
- package/dist/__tests__/integration/clipComposite.test.d.ts.map +1 -0
- package/dist/__tests__/integration/clipComposite.test.js +56 -0
- package/dist/__tests__/integration/clipComposite.test.js.map +1 -0
- package/dist/__tests__/integration/faceDetection.test.d.ts +2 -0
- package/dist/__tests__/integration/faceDetection.test.d.ts.map +1 -0
- package/dist/__tests__/integration/faceDetection.test.js +85 -0
- package/dist/__tests__/integration/faceDetection.test.js.map +1 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.d.ts +2 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.d.ts.map +1 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.js +88 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.js.map +1 -0
- package/dist/__tests__/integration/fixture.d.ts +19 -0
- package/dist/__tests__/integration/fixture.d.ts.map +1 -0
- package/dist/__tests__/integration/fixture.js +112 -0
- package/dist/__tests__/integration/fixture.js.map +1 -0
- package/dist/__tests__/integration/fixture.test.d.ts +2 -0
- package/dist/__tests__/integration/fixture.test.d.ts.map +1 -0
- package/dist/__tests__/integration/fixture.test.js +27 -0
- package/dist/__tests__/integration/fixture.test.js.map +1 -0
- package/dist/__tests__/integration/realCaptions.test.d.ts +2 -0
- package/dist/__tests__/integration/realCaptions.test.d.ts.map +1 -0
- package/dist/__tests__/integration/realCaptions.test.js +226 -0
- package/dist/__tests__/integration/realCaptions.test.js.map +1 -0
- package/dist/__tests__/integration/realPipeline.test.d.ts +2 -0
- package/dist/__tests__/integration/realPipeline.test.d.ts.map +1 -0
- package/dist/__tests__/integration/realPipeline.test.js +210 -0
- package/dist/__tests__/integration/realPipeline.test.js.map +1 -0
- package/dist/__tests__/integration/silenceRemoval.test.d.ts +2 -0
- package/dist/__tests__/integration/silenceRemoval.test.d.ts.map +1 -0
- package/dist/__tests__/integration/silenceRemoval.test.js +93 -0
- package/dist/__tests__/integration/silenceRemoval.test.js.map +1 -0
- package/dist/__tests__/pipeline.test.d.ts +2 -0
- package/dist/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline.test.js +434 -0
- package/dist/__tests__/pipeline.test.js.map +1 -0
- package/dist/__tests__/services.test.d.ts +2 -0
- package/dist/__tests__/services.test.d.ts.map +1 -0
- package/dist/__tests__/services.test.js +655 -0
- package/dist/__tests__/services.test.js.map +1 -0
- package/dist/__tests__/silenceRemoval.test.d.ts +2 -0
- package/dist/__tests__/silenceRemoval.test.d.ts.map +1 -0
- package/dist/__tests__/silenceRemoval.test.js +266 -0
- package/dist/__tests__/silenceRemoval.test.js.map +1 -0
- package/dist/__tests__/singlePassEdit.test.d.ts +2 -0
- package/dist/__tests__/singlePassEdit.test.d.ts.map +1 -0
- package/dist/__tests__/singlePassEdit.test.js +321 -0
- package/dist/__tests__/singlePassEdit.test.js.map +1 -0
- package/dist/__tests__/smoke.test.d.ts +2 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +8 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/__tests__/utilities.test.d.ts +2 -0
- package/dist/__tests__/utilities.test.d.ts.map +1 -0
- package/dist/__tests__/utilities.test.js +268 -0
- package/dist/__tests__/utilities.test.js.map +1 -0
- package/dist/agents/BaseAgent.d.ts +52 -0
- package/dist/agents/BaseAgent.d.ts.map +1 -0
- package/dist/agents/BaseAgent.js +108 -0
- package/dist/agents/BaseAgent.js.map +1 -0
- package/dist/agents/BlogAgent.d.ts +3 -0
- package/dist/agents/BlogAgent.d.ts.map +1 -0
- package/dist/agents/BlogAgent.js +163 -0
- package/dist/agents/BlogAgent.js.map +1 -0
- package/dist/agents/ChapterAgent.d.ts +11 -0
- package/dist/agents/ChapterAgent.d.ts.map +1 -0
- package/dist/agents/ChapterAgent.js +191 -0
- package/dist/agents/ChapterAgent.js.map +1 -0
- package/dist/agents/MediumVideoAgent.d.ts +3 -0
- package/dist/agents/MediumVideoAgent.d.ts.map +1 -0
- package/dist/agents/MediumVideoAgent.js +219 -0
- package/dist/agents/MediumVideoAgent.js.map +1 -0
- package/dist/agents/ShortsAgent.d.ts +3 -0
- package/dist/agents/ShortsAgent.d.ts.map +1 -0
- package/dist/agents/ShortsAgent.js +243 -0
- package/dist/agents/ShortsAgent.js.map +1 -0
- package/dist/agents/SilenceRemovalAgent.d.ts +9 -0
- package/dist/agents/SilenceRemovalAgent.d.ts.map +1 -0
- package/dist/agents/SilenceRemovalAgent.js +208 -0
- package/dist/agents/SilenceRemovalAgent.js.map +1 -0
- package/dist/agents/SocialMediaAgent.d.ts +4 -0
- package/dist/agents/SocialMediaAgent.d.ts.map +1 -0
- package/dist/agents/SocialMediaAgent.js +248 -0
- package/dist/agents/SocialMediaAgent.js.map +1 -0
- package/dist/agents/SummaryAgent.d.ts +11 -0
- package/dist/agents/SummaryAgent.d.ts.map +1 -0
- package/dist/agents/SummaryAgent.js +333 -0
- package/dist/agents/SummaryAgent.js.map +1 -0
- package/dist/config/brand.d.ts +29 -0
- package/dist/config/brand.d.ts.map +1 -0
- package/dist/config/brand.js +83 -0
- package/dist/config/brand.js.map +1 -0
- package/dist/config/environment.d.ts +36 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +44 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/logger.d.ts +5 -0
- package/dist/config/logger.d.ts.map +1 -0
- package/dist/config/logger.js +13 -0
- package/dist/config/logger.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline.d.ts +57 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +287 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/services/captionGeneration.d.ts +7 -0
- package/dist/services/captionGeneration.d.ts.map +1 -0
- package/dist/services/captionGeneration.js +29 -0
- package/dist/services/captionGeneration.js.map +1 -0
- package/dist/services/fileWatcher.d.ts +19 -0
- package/dist/services/fileWatcher.d.ts.map +1 -0
- package/dist/services/fileWatcher.js +120 -0
- package/dist/services/fileWatcher.js.map +1 -0
- package/dist/services/gitOperations.d.ts +3 -0
- package/dist/services/gitOperations.d.ts.map +1 -0
- package/dist/services/gitOperations.js +43 -0
- package/dist/services/gitOperations.js.map +1 -0
- package/dist/services/socialPosting.d.ts +38 -0
- package/dist/services/socialPosting.d.ts.map +1 -0
- package/dist/services/socialPosting.js +102 -0
- package/dist/services/socialPosting.js.map +1 -0
- package/dist/services/transcription.d.ts +3 -0
- package/dist/services/transcription.d.ts.map +1 -0
- package/dist/services/transcription.js +100 -0
- package/dist/services/transcription.js.map +1 -0
- package/dist/services/videoIngestion.d.ts +3 -0
- package/dist/services/videoIngestion.d.ts.map +1 -0
- package/dist/services/videoIngestion.js +103 -0
- package/dist/services/videoIngestion.js.map +1 -0
- package/dist/tools/captions/captionGenerator.d.ts +84 -0
- package/dist/tools/captions/captionGenerator.d.ts.map +1 -0
- package/dist/tools/captions/captionGenerator.js +390 -0
- package/dist/tools/captions/captionGenerator.js.map +1 -0
- package/dist/tools/ffmpeg/aspectRatio.d.ts +101 -0
- package/dist/tools/ffmpeg/aspectRatio.d.ts.map +1 -0
- package/dist/tools/ffmpeg/aspectRatio.js +338 -0
- package/dist/tools/ffmpeg/aspectRatio.js.map +1 -0
- package/dist/tools/ffmpeg/audioExtraction.d.ts +16 -0
- package/dist/tools/ffmpeg/audioExtraction.d.ts.map +1 -0
- package/dist/tools/ffmpeg/audioExtraction.js +86 -0
- package/dist/tools/ffmpeg/audioExtraction.js.map +1 -0
- package/dist/tools/ffmpeg/captionBurning.d.ts +8 -0
- package/dist/tools/ffmpeg/captionBurning.d.ts.map +1 -0
- package/dist/tools/ffmpeg/captionBurning.js +71 -0
- package/dist/tools/ffmpeg/captionBurning.js.map +1 -0
- package/dist/tools/ffmpeg/clipExtraction.d.ts +23 -0
- package/dist/tools/ffmpeg/clipExtraction.d.ts.map +1 -0
- package/dist/tools/ffmpeg/clipExtraction.js +178 -0
- package/dist/tools/ffmpeg/clipExtraction.js.map +1 -0
- package/dist/tools/ffmpeg/faceDetection.d.ts +127 -0
- package/dist/tools/ffmpeg/faceDetection.d.ts.map +1 -0
- package/dist/tools/ffmpeg/faceDetection.js +500 -0
- package/dist/tools/ffmpeg/faceDetection.js.map +1 -0
- package/dist/tools/ffmpeg/frameCapture.d.ts +10 -0
- package/dist/tools/ffmpeg/frameCapture.d.ts.map +1 -0
- package/dist/tools/ffmpeg/frameCapture.js +48 -0
- package/dist/tools/ffmpeg/frameCapture.js.map +1 -0
- package/dist/tools/ffmpeg/silenceDetection.d.ts +10 -0
- package/dist/tools/ffmpeg/silenceDetection.d.ts.map +1 -0
- package/dist/tools/ffmpeg/silenceDetection.js +55 -0
- package/dist/tools/ffmpeg/silenceDetection.js.map +1 -0
- package/dist/tools/ffmpeg/singlePassEdit.d.ts +25 -0
- package/dist/tools/ffmpeg/singlePassEdit.d.ts.map +1 -0
- package/dist/tools/ffmpeg/singlePassEdit.js +123 -0
- package/dist/tools/ffmpeg/singlePassEdit.js.map +1 -0
- package/dist/tools/search/exaClient.d.ts +8 -0
- package/dist/tools/search/exaClient.d.ts.map +1 -0
- package/dist/tools/search/exaClient.js +38 -0
- package/dist/tools/search/exaClient.js.map +1 -0
- package/dist/tools/whisper/whisperClient.d.ts +3 -0
- package/dist/tools/whisper/whisperClient.d.ts.map +1 -0
- package/dist/tools/whisper/whisperClient.js +77 -0
- package/dist/tools/whisper/whisperClient.js.map +1 -0
- package/dist/types/index.d.ts +305 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +44 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import ffmpeg from 'fluent-ffmpeg';
|
|
2
|
+
import { execFile } from 'child_process';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
import pathMod from 'path';
|
|
5
|
+
import logger from '../../config/logger';
|
|
6
|
+
const ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg';
|
|
7
|
+
const ffprobePath = process.env.FFPROBE_PATH || 'ffprobe';
|
|
8
|
+
ffmpeg.setFfmpegPath(ffmpegPath);
|
|
9
|
+
ffmpeg.setFfprobePath(ffprobePath);
|
|
10
|
+
/**
|
|
11
|
+
* Extract a single clip segment using stream copy (-c copy) for speed.
|
|
12
|
+
* @param buffer Seconds of padding added before start and after end (default 1.0)
|
|
13
|
+
*/
|
|
14
|
+
export async function extractClip(videoPath, start, end, outputPath, buffer = 1.0) {
|
|
15
|
+
const outputDir = pathMod.dirname(outputPath);
|
|
16
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
17
|
+
const bufferedStart = Math.max(0, start - buffer);
|
|
18
|
+
const bufferedEnd = end + buffer;
|
|
19
|
+
const duration = bufferedEnd - bufferedStart;
|
|
20
|
+
logger.info(`Extracting clip [${start}s–${end}s] (buffered: ${bufferedStart.toFixed(2)}s–${bufferedEnd.toFixed(2)}s) → ${outputPath}`);
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
ffmpeg(videoPath)
|
|
23
|
+
.setStartTime(bufferedStart)
|
|
24
|
+
.setDuration(duration)
|
|
25
|
+
.outputOptions('-c copy')
|
|
26
|
+
.outputOptions('-avoid_negative_ts make_zero')
|
|
27
|
+
.output(outputPath)
|
|
28
|
+
.on('end', () => {
|
|
29
|
+
logger.info(`Clip extraction complete: ${outputPath}`);
|
|
30
|
+
resolve(outputPath);
|
|
31
|
+
})
|
|
32
|
+
.on('error', (err) => {
|
|
33
|
+
logger.error(`Clip extraction failed: ${err.message}`);
|
|
34
|
+
reject(new Error(`Clip extraction failed: ${err.message}`));
|
|
35
|
+
})
|
|
36
|
+
.run();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Extract multiple non-contiguous segments and concatenate them into one clip.
|
|
41
|
+
* Each segment is padded by `buffer` seconds on both sides for smoother cuts.
|
|
42
|
+
* Re-encodes and uses concat demuxer for clean joins.
|
|
43
|
+
* @param buffer Seconds of padding added before start and after end of each segment (default 1.0)
|
|
44
|
+
*/
|
|
45
|
+
export async function extractCompositeClip(videoPath, segments, outputPath, buffer = 1.0) {
|
|
46
|
+
if (!segments || segments.length === 0) {
|
|
47
|
+
throw new Error('At least one segment is required');
|
|
48
|
+
}
|
|
49
|
+
if (segments.length === 1) {
|
|
50
|
+
return extractClip(videoPath, segments[0].start, segments[0].end, outputPath, buffer);
|
|
51
|
+
}
|
|
52
|
+
const outputDir = pathMod.dirname(outputPath);
|
|
53
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
54
|
+
const tempDir = pathMod.join(outputDir, `.temp-${Date.now()}`);
|
|
55
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
56
|
+
const tempFiles = [];
|
|
57
|
+
try {
|
|
58
|
+
// Extract each segment to a temp file (re-encode for reliable concat)
|
|
59
|
+
for (let i = 0; i < segments.length; i++) {
|
|
60
|
+
const seg = segments[i];
|
|
61
|
+
const tempPath = pathMod.join(tempDir, `segment-${i}.mp4`);
|
|
62
|
+
tempFiles.push(tempPath);
|
|
63
|
+
const bufferedStart = Math.max(0, seg.start - buffer);
|
|
64
|
+
const bufferedEnd = seg.end + buffer;
|
|
65
|
+
logger.info(`Extracting segment ${i + 1}/${segments.length} [${seg.start}s–${seg.end}s] (buffered: ${bufferedStart.toFixed(2)}s–${bufferedEnd.toFixed(2)}s)`);
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
ffmpeg(videoPath)
|
|
68
|
+
.setStartTime(bufferedStart)
|
|
69
|
+
.setDuration(bufferedEnd - bufferedStart)
|
|
70
|
+
.outputOptions(['-threads', '4', '-preset', 'ultrafast'])
|
|
71
|
+
.output(tempPath)
|
|
72
|
+
.on('end', () => resolve())
|
|
73
|
+
.on('error', (err) => reject(new Error(`Segment ${i} extraction failed: ${err.message}`)))
|
|
74
|
+
.run();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Build concat list file
|
|
78
|
+
const concatListPath = pathMod.join(tempDir, 'concat-list.txt');
|
|
79
|
+
const listContent = tempFiles.map((f) => `file '${f.replace(/'/g, "'\\''")}'`).join('\n');
|
|
80
|
+
await fs.writeFile(concatListPath, listContent);
|
|
81
|
+
// Concatenate segments (re-encode for clean joins across buffered segments)
|
|
82
|
+
logger.info(`Concatenating ${segments.length} segments → ${outputPath}`);
|
|
83
|
+
await new Promise((resolve, reject) => {
|
|
84
|
+
ffmpeg()
|
|
85
|
+
.input(concatListPath)
|
|
86
|
+
.inputOptions(['-f', 'concat', '-safe', '0'])
|
|
87
|
+
.outputOptions(['-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '23', '-threads', '4', '-c:a', 'aac'])
|
|
88
|
+
.output(outputPath)
|
|
89
|
+
.on('end', () => resolve())
|
|
90
|
+
.on('error', (err) => reject(new Error(`Concat failed: ${err.message}`)))
|
|
91
|
+
.run();
|
|
92
|
+
});
|
|
93
|
+
logger.info(`Composite clip complete: ${outputPath}`);
|
|
94
|
+
return outputPath;
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
// Clean up temp files
|
|
98
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Extract multiple non-contiguous segments and concatenate them with crossfade
|
|
103
|
+
* transitions using FFmpeg xfade/acrossfade filters.
|
|
104
|
+
* Falls back to extractCompositeClip if only one segment is provided.
|
|
105
|
+
*
|
|
106
|
+
* @param transitionDuration Crossfade duration in seconds (default 0.5)
|
|
107
|
+
* @param buffer Seconds of padding added before/after each segment (default 1.0)
|
|
108
|
+
*/
|
|
109
|
+
export async function extractCompositeClipWithTransitions(videoPath, segments, outputPath, transitionDuration = 0.5, buffer = 1.0) {
|
|
110
|
+
if (!segments || segments.length === 0) {
|
|
111
|
+
throw new Error('At least one segment is required');
|
|
112
|
+
}
|
|
113
|
+
// Single segment — no transitions needed
|
|
114
|
+
if (segments.length === 1) {
|
|
115
|
+
return extractClip(videoPath, segments[0].start, segments[0].end, outputPath, buffer);
|
|
116
|
+
}
|
|
117
|
+
// Two segments — no transitions needed, use regular composite
|
|
118
|
+
if (segments.length === 2 && transitionDuration <= 0) {
|
|
119
|
+
return extractCompositeClip(videoPath, segments, outputPath, buffer);
|
|
120
|
+
}
|
|
121
|
+
const outputDir = pathMod.dirname(outputPath);
|
|
122
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
123
|
+
// Build filter_complex for xfade transitions between segments
|
|
124
|
+
const filterParts = [];
|
|
125
|
+
const segDurations = [];
|
|
126
|
+
for (let i = 0; i < segments.length; i++) {
|
|
127
|
+
const seg = segments[i];
|
|
128
|
+
const bufferedStart = Math.max(0, seg.start - buffer);
|
|
129
|
+
const bufferedEnd = seg.end + buffer;
|
|
130
|
+
const duration = bufferedEnd - bufferedStart;
|
|
131
|
+
segDurations.push(duration);
|
|
132
|
+
filterParts.push(`[0:v]trim=start=${bufferedStart.toFixed(3)}:end=${bufferedEnd.toFixed(3)},setpts=PTS-STARTPTS[v${i}]`);
|
|
133
|
+
filterParts.push(`[0:a]atrim=start=${bufferedStart.toFixed(3)}:end=${bufferedEnd.toFixed(3)},asetpts=PTS-STARTPTS[a${i}]`);
|
|
134
|
+
}
|
|
135
|
+
// Chain xfade transitions: [v0][v1]xfade → [xv0]; [xv0][v2]xfade → [xv1]; ...
|
|
136
|
+
let prevVideo = 'v0';
|
|
137
|
+
let prevAudio = 'a0';
|
|
138
|
+
let cumulativeDuration = segDurations[0];
|
|
139
|
+
for (let i = 1; i < segments.length; i++) {
|
|
140
|
+
const offset = Math.max(0, cumulativeDuration - transitionDuration);
|
|
141
|
+
const outVideo = i === segments.length - 1 ? 'vout' : `xv${i - 1}`;
|
|
142
|
+
const outAudio = i === segments.length - 1 ? 'aout' : `xa${i - 1}`;
|
|
143
|
+
filterParts.push(`[${prevVideo}][v${i}]xfade=transition=fade:duration=${transitionDuration.toFixed(3)}:offset=${offset.toFixed(3)}[${outVideo}]`);
|
|
144
|
+
filterParts.push(`[${prevAudio}][a${i}]acrossfade=d=${transitionDuration.toFixed(3)}[${outAudio}]`);
|
|
145
|
+
prevVideo = outVideo;
|
|
146
|
+
prevAudio = outAudio;
|
|
147
|
+
// After xfade, the combined duration shrinks by transitionDuration
|
|
148
|
+
cumulativeDuration = cumulativeDuration - transitionDuration + segDurations[i];
|
|
149
|
+
}
|
|
150
|
+
const filterComplex = filterParts.join(';\n');
|
|
151
|
+
const args = [
|
|
152
|
+
'-y',
|
|
153
|
+
'-i', videoPath,
|
|
154
|
+
'-filter_complex', filterComplex,
|
|
155
|
+
'-map', '[vout]',
|
|
156
|
+
'-map', '[aout]',
|
|
157
|
+
'-c:v', 'libx264',
|
|
158
|
+
'-preset', 'ultrafast',
|
|
159
|
+
'-crf', '23',
|
|
160
|
+
'-threads', '4',
|
|
161
|
+
'-c:a', 'aac',
|
|
162
|
+
'-b:a', '128k',
|
|
163
|
+
outputPath,
|
|
164
|
+
];
|
|
165
|
+
logger.info(`[ClipExtraction] Compositing ${segments.length} segments with xfade transitions → ${outputPath}`);
|
|
166
|
+
return new Promise((resolve, reject) => {
|
|
167
|
+
execFile(ffmpegPath, args, { maxBuffer: 50 * 1024 * 1024 }, (error, _stdout, stderr) => {
|
|
168
|
+
if (error) {
|
|
169
|
+
logger.error(`[ClipExtraction] xfade composite failed: ${stderr}`);
|
|
170
|
+
reject(new Error(`xfade composite clip failed: ${error.message}`));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
logger.info(`[ClipExtraction] xfade composite complete: ${outputPath}`);
|
|
174
|
+
resolve(outputPath);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=clipExtraction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipExtraction.js","sourceRoot":"","sources":["../../../src/tools/ffmpeg/clipExtraction.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,OAAO,MAAM,MAAM,CAAC;AAC3B,OAAO,MAAM,MAAM,qBAAqB,CAAC;AAGzC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,QAAQ,CAAC;AACvD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,SAAS,CAAC;AAC1D,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;AACjC,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,KAAa,EACb,GAAW,EACX,UAAkB,EAClB,SAAiB,GAAG;IAEpB,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,GAAG,GAAG,MAAM,CAAC;IACjC,MAAM,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;IAC7C,MAAM,CAAC,IAAI,CAAC,oBAAoB,KAAK,KAAK,GAAG,iBAAiB,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,UAAU,EAAE,CAAC,CAAC;IAEvI,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,MAAM,CAAC,SAAS,CAAC;aACd,YAAY,CAAC,aAAa,CAAC;aAC3B,WAAW,CAAC,QAAQ,CAAC;aACrB,aAAa,CAAC,SAAS,CAAC;aACxB,aAAa,CAAC,8BAA8B,CAAC;aAC7C,MAAM,CAAC,UAAU,CAAC;aAClB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACd,MAAM,CAAC,IAAI,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,CAAC;aACD,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACnB,MAAM,CAAC,KAAK,CAAC,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACvD,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC;aACD,GAAG,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,QAAwB,EACxB,UAAkB,EAClB,SAAiB,GAAG;IAEpB,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,IAAI,CAAC;QACH,sEAAsE;QACtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;YAC3D,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEzB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;YACtD,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,GAAG,iBAAiB,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAE9J,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,CAAC,SAAS,CAAC;qBACd,YAAY,CAAC,aAAa,CAAC;qBAC3B,WAAW,CAAC,WAAW,GAAG,aAAa,CAAC;qBACxC,aAAa,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;qBACxD,MAAM,CAAC,QAAQ,CAAC;qBAChB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;qBAC1B,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,uBAAuB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;qBACzF,GAAG,EAAE,CAAC;YACX,CAAC,CAAC,CAAC;QACL,CAAC;QAED,yBAAyB;QACzB,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1F,MAAM,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QAEhD,4EAA4E;QAC5E,MAAM,CAAC,IAAI,CAAC,iBAAiB,QAAQ,CAAC,MAAM,eAAe,UAAU,EAAE,CAAC,CAAC;QACzE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,EAAE;iBACL,KAAK,CAAC,cAAc,CAAC;iBACrB,YAAY,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;iBAC5C,aAAa,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;iBACxG,MAAM,CAAC,UAAU,CAAC;iBAClB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;iBAC1B,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;iBACxE,GAAG,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,sBAAsB;QACtB,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,SAAiB,EACjB,QAAwB,EACxB,UAAkB,EAClB,qBAA6B,GAAG,EAChC,SAAiB,GAAG;IAEpB,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,yCAAyC;IACzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IACxF,CAAC;IAED,8DAA8D;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,kBAAkB,IAAI,CAAC,EAAE,CAAC;QACrD,OAAO,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,8DAA8D;IAC9D,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC;QACrC,MAAM,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;QAC7C,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE5B,WAAW,CAAC,IAAI,CACd,mBAAmB,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,GAAG,CACvG,CAAC;QACF,WAAW,CAAC,IAAI,CACd,oBAAoB,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,GAAG,CACzG,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,kBAAkB,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAEzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACnE,MAAM,QAAQ,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAEnE,WAAW,CAAC,IAAI,CACd,IAAI,SAAS,MAAM,CAAC,mCAAmC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,CAChI,CAAC;QACF,WAAW,CAAC,IAAI,CACd,IAAI,SAAS,MAAM,CAAC,iBAAiB,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,CAClF,CAAC;QAEF,SAAS,GAAG,QAAQ,CAAC;QACrB,SAAS,GAAG,QAAQ,CAAC;QACrB,mEAAmE;QACnE,kBAAkB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,aAAa,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAG;QACX,IAAI;QACJ,IAAI,EAAE,SAAS;QACf,iBAAiB,EAAE,aAAa;QAChC,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,WAAW;QACtB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,GAAG;QACf,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,MAAM;QACd,UAAU;KACX,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,gCAAgC,QAAQ,CAAC,MAAM,sCAAsC,UAAU,EAAE,CAAC,CAAC;IAE/G,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;YACrF,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,KAAK,CAAC,4CAA4C,MAAM,EAAE,CAAC,CAAC;gBACnE,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACnE,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,8CAA8C,UAAU,EAAE,CAAC,CAAC;YACxE,OAAO,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounding box and metadata for a detected webcam overlay in a screen recording.
|
|
3
|
+
*
|
|
4
|
+
* @property x - Left edge in pixels (original video resolution)
|
|
5
|
+
* @property y - Top edge in pixels (original video resolution)
|
|
6
|
+
* @property width - Width of the webcam region in pixels
|
|
7
|
+
* @property height - Height of the webcam region in pixels
|
|
8
|
+
* @property position - Which corner of the frame the webcam occupies
|
|
9
|
+
* @property confidence - Detection confidence 0–1 (combines skin-tone consistency
|
|
10
|
+
* across frames with per-frame score strength)
|
|
11
|
+
*/
|
|
12
|
+
export interface WebcamRegion {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
18
|
+
confidence: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Per-frame analysis result for a single corner region.
|
|
22
|
+
*
|
|
23
|
+
* @property skinToneRatio - Fraction of pixels matching the skin-tone heuristic (0–1)
|
|
24
|
+
* @property variance - Average RGB channel variance — high variance means the
|
|
25
|
+
* region has complex visual content (a face), low variance means a solid
|
|
26
|
+
* background or static UI element.
|
|
27
|
+
*/
|
|
28
|
+
export interface CornerAnalysis {
|
|
29
|
+
position: WebcamRegion['position'];
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
skinToneRatio: number;
|
|
35
|
+
variance: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function getVideoResolution(videoPath: string): Promise<{
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Check if a pixel (in RGB) falls within skin-tone range.
|
|
43
|
+
* Uses simplified HSV heuristic: hue ~0-50°, moderate saturation.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isSkinTone(r: number, g: number, b: number): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Find the position with the largest intensity step between adjacent elements.
|
|
48
|
+
*
|
|
49
|
+
* "Peak difference" = the index where `|means[i+1] - means[i]|` is maximized
|
|
50
|
+
* within the search range. This corresponds to the webcam overlay's edge,
|
|
51
|
+
* because the overlay border creates a hard brightness transition that
|
|
52
|
+
* persists across all frames, while content-based edges average out.
|
|
53
|
+
*
|
|
54
|
+
* @param means - 1-D array of averaged intensities (from column or row means)
|
|
55
|
+
* @param searchFrom - Start of search range (inclusive)
|
|
56
|
+
* @param searchTo - End of search range (inclusive)
|
|
57
|
+
* @param minDiff - Minimum step magnitude to accept (rejects noise)
|
|
58
|
+
* @returns `{index, magnitude}` — index of the edge, or -1 if no edge exceeds minDiff
|
|
59
|
+
*/
|
|
60
|
+
export declare function findPeakDiff(means: Float64Array, searchFrom: number, searchTo: number, minDiff: number): {
|
|
61
|
+
index: number;
|
|
62
|
+
magnitude: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Refine the webcam bounding box by detecting the overlay's spatial edges.
|
|
66
|
+
*
|
|
67
|
+
* ### Why refinement is needed
|
|
68
|
+
* The coarse phase ({@link detectWebcamRegion}'s corner analysis) only identifies
|
|
69
|
+
* which corner contains a webcam — it uses a fixed 25% region and doesn't know
|
|
70
|
+
* the overlay's exact boundaries. Refinement finds pixel-accurate edges.
|
|
71
|
+
*
|
|
72
|
+
* ### Edge detection algorithm
|
|
73
|
+
* 1. For each sample frame, compute **per-column** and **per-row** mean grayscale
|
|
74
|
+
* intensities (restricted to the webcam's half of the frame for stronger signal).
|
|
75
|
+
* 2. **Average across all frames** — the overlay border is spatially fixed and
|
|
76
|
+
* produces a consistent intensity step, while changing video content (slides,
|
|
77
|
+
* code, etc.) averages out to a smooth gradient. This is the key insight that
|
|
78
|
+
* makes the approach work without traditional edge detection filters.
|
|
79
|
+
* 3. Use {@link findPeakDiff} to locate the maximum inter-adjacent intensity
|
|
80
|
+
* difference in the averaged signal — this is the overlay's vertical and
|
|
81
|
+
* horizontal edge.
|
|
82
|
+
* 4. Sanity-check: the resulting rectangle must be 5–55% of the frame in each
|
|
83
|
+
* dimension (webcams are never tiny or most of the screen).
|
|
84
|
+
*
|
|
85
|
+
* @param framePaths - Paths to sample frames at analysis resolution (320×180)
|
|
86
|
+
* @param position - Which corner contains the webcam (from coarse phase)
|
|
87
|
+
* @returns Refined bounding box in analysis-resolution coordinates, or null
|
|
88
|
+
* if no strong edges are found or the result is implausibly sized
|
|
89
|
+
*/
|
|
90
|
+
export declare function refineBoundingBox(framePaths: string[], position: WebcamRegion['position']): Promise<{
|
|
91
|
+
x: number;
|
|
92
|
+
y: number;
|
|
93
|
+
width: number;
|
|
94
|
+
height: number;
|
|
95
|
+
} | null>;
|
|
96
|
+
/**
|
|
97
|
+
* Calculate confidence that a corner contains a webcam overlay based on
|
|
98
|
+
* per-frame scores. Combines consistency (fraction of non-zero frames) with
|
|
99
|
+
* average score.
|
|
100
|
+
*/
|
|
101
|
+
export declare function calculateCornerConfidence(scores: number[]): number;
|
|
102
|
+
/**
|
|
103
|
+
* Detect a webcam overlay region in a screen recording.
|
|
104
|
+
*
|
|
105
|
+
* ### Two-phase approach
|
|
106
|
+
*
|
|
107
|
+
* **Phase 1 — Coarse corner scan:**
|
|
108
|
+
* Samples 5 frames at even intervals across the video and analyzes each of the
|
|
109
|
+
* four corners (25% × 25% regions) for skin-tone pixels and visual variance.
|
|
110
|
+
* A corner with consistent skin-tone presence across multiple frames is likely
|
|
111
|
+
* a webcam overlay. The scoring formula weights skin ratio by variance — webcam
|
|
112
|
+
* corners are visually busy (a moving face), while solid-color UI elements
|
|
113
|
+
* (like a colored status bar) have low variance even if they match skin tones.
|
|
114
|
+
*
|
|
115
|
+
* **Phase 2 — Refined edge detection ({@link refineBoundingBox}):**
|
|
116
|
+
* Once we know which corner, we find the overlay's exact pixel boundaries by
|
|
117
|
+
* looking for persistent intensity edges across frames.
|
|
118
|
+
*
|
|
119
|
+
* All analysis is performed on downscaled frames (320×180) for speed, then
|
|
120
|
+
* results are scaled back to the original video resolution.
|
|
121
|
+
*
|
|
122
|
+
* @param videoPath - Path to the source video file
|
|
123
|
+
* @returns The detected webcam region in original video resolution, or null
|
|
124
|
+
* if no webcam overlay is found with sufficient confidence
|
|
125
|
+
*/
|
|
126
|
+
export declare function detectWebcamRegion(videoPath: string): Promise<WebcamRegion | null>;
|
|
127
|
+
//# sourceMappingURL=faceDetection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faceDetection.d.ts","sourceRoot":"","sources":["../../../src/tools/ffmpeg/faceDetection.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAA;IACnE,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAA;IAClC,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAiDD,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBtG;AA4CD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAWnE;AA8ID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GACzE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAUtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,GACjC,OAAO,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAoEzE;AAID;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAMlE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmGxF"}
|