dsh-pf-stream 0.0.0-stage → 0.1.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.
- package/LICENSE +201 -0
- package/README.en.md +110 -0
- package/README.md +109 -2
- package/cordis.patch.yml +2 -0
- package/lib/entry/adapter.d.ts +21 -0
- package/lib/entry/adapter.js +112 -0
- package/lib/entry/adapter.js.map +1 -0
- package/lib/entry/legacyNormalizer.d.ts +16 -0
- package/lib/entry/legacyNormalizer.js +86 -0
- package/lib/entry/legacyNormalizer.js.map +1 -0
- package/lib/errors.d.ts +7 -0
- package/lib/errors.js +12 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +108 -0
- package/lib/index.js +109 -0
- package/lib/index.js.map +1 -0
- package/lib/session/state.d.ts +25 -0
- package/lib/session/state.js +255 -0
- package/lib/session/state.js.map +1 -0
- package/lib/stage0/decoder.d.ts +57 -0
- package/lib/stage0/decoder.js +229 -0
- package/lib/stage0/decoder.js.map +1 -0
- package/lib/stage0/ffmpeg.d.ts +34 -0
- package/lib/stage0/ffmpeg.js +90 -0
- package/lib/stage0/ffmpeg.js.map +1 -0
- package/lib/stage0/probe.d.ts +47 -0
- package/lib/stage0/probe.js +150 -0
- package/lib/stage0/probe.js.map +1 -0
- package/lib/stage1/gradient.d.ts +27 -0
- package/lib/stage1/gradient.js +85 -0
- package/lib/stage1/gradient.js.map +1 -0
- package/lib/stage1/perceive.d.ts +66 -0
- package/lib/stage1/perceive.js +233 -0
- package/lib/stage1/perceive.js.map +1 -0
- package/lib/stage2/stateMachine.d.ts +70 -0
- package/lib/stage2/stateMachine.js +180 -0
- package/lib/stage2/stateMachine.js.map +1 -0
- package/lib/stage2/switchDetector.d.ts +18 -0
- package/lib/stage2/switchDetector.js +34 -0
- package/lib/stage2/switchDetector.js.map +1 -0
- package/lib/stage3/deterministicRouter.d.ts +44 -0
- package/lib/stage3/deterministicRouter.js +203 -0
- package/lib/stage3/deterministicRouter.js.map +1 -0
- package/lib/stage3/streamSampler.d.ts +74 -0
- package/lib/stage3/streamSampler.js +497 -0
- package/lib/stage3/streamSampler.js.map +1 -0
- package/lib/stage4/canvasPacker.d.ts +60 -0
- package/lib/stage4/canvasPacker.js +161 -0
- package/lib/stage4/canvasPacker.js.map +1 -0
- package/lib/stage4/markers.d.ts +19 -0
- package/lib/stage4/markers.js +98 -0
- package/lib/stage4/markers.js.map +1 -0
- package/lib/stage4/viewport.d.ts +61 -0
- package/lib/stage4/viewport.js +151 -0
- package/lib/stage4/viewport.js.map +1 -0
- package/lib/stage5/controller.d.ts +61 -0
- package/lib/stage5/controller.js +260 -0
- package/lib/stage5/controller.js.map +1 -0
- package/lib/stage5/intent.d.ts +27 -0
- package/lib/stage5/intent.js +87 -0
- package/lib/stage5/intent.js.map +1 -0
- package/lib/stage6/history.d.ts +52 -0
- package/lib/stage6/history.js +155 -0
- package/lib/stage6/history.js.map +1 -0
- package/lib/stage6/precheck.d.ts +56 -0
- package/lib/stage6/precheck.js +86 -0
- package/lib/stage6/precheck.js.map +1 -0
- package/lib/tool/handler.d.ts +35 -0
- package/lib/tool/handler.js +56 -0
- package/lib/tool/handler.js.map +1 -0
- package/lib/tool/schema.d.ts +29 -0
- package/lib/tool/schema.js +59 -0
- package/lib/tool/schema.js.map +1 -0
- package/lib/tool/streamCall.d.ts +41 -0
- package/lib/tool/streamCall.js +105 -0
- package/lib/tool/streamCall.js.map +1 -0
- package/lib/types.d.ts +355 -0
- package/lib/types.js +11 -0
- package/lib/types.js.map +1 -0
- package/lib/utils/ffmpegBin.d.ts +20 -0
- package/lib/utils/ffmpegBin.js +62 -0
- package/lib/utils/ffmpegBin.js.map +1 -0
- package/lib/utils/hash.d.ts +13 -0
- package/lib/utils/hash.js +24 -0
- package/lib/utils/hash.js.map +1 -0
- package/lib/utils/image.d.ts +32 -0
- package/lib/utils/image.js +135 -0
- package/lib/utils/image.js.map +1 -0
- package/lib/utils/pts.d.ts +15 -0
- package/lib/utils/pts.js +50 -0
- package/lib/utils/pts.js.map +1 -0
- package/package.json +55 -4
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage 0 —— 双模解码调度(契约 3)。
|
|
3
|
+
*
|
|
4
|
+
* - seek_points(定点寻址):目标 PTS 排序去重后逐点 `ffmpeg -ss <pts> -i <file> -frames:v 1`
|
|
5
|
+
* 关键帧跳跃 + 精解码,全分辨率(保障 OCR 级细节);
|
|
6
|
+
* - 批量退化:目标数 ≥32 且平均间隔 ≤2s 时,排序后单遍区间解码摊薄寻址开销;
|
|
7
|
+
* - 空帧回退:解码端空输出时回退复用最后一个有效解码帧(契约 3);
|
|
8
|
+
* - 解码输出天然处于显示坐标系(ffmpeg 默认 autorotate)。
|
|
9
|
+
*
|
|
10
|
+
* decodeRangeStreaming 为单遍流式消费(契约 3:每帧解码后同步派生感知图,
|
|
11
|
+
* 当场裁剪缩放入队并释放原图;await onFrame 期间 stdout 异步迭代自动背压)。
|
|
12
|
+
*/
|
|
13
|
+
import type { RgbImage } from '../utils/image.js';
|
|
14
|
+
export interface DecodePointOptions {
|
|
15
|
+
ffmpegPath: string;
|
|
16
|
+
displayWidth: number;
|
|
17
|
+
displayHeight: number;
|
|
18
|
+
signal?: AbortSignal | undefined;
|
|
19
|
+
}
|
|
20
|
+
export type DecodePointFn = typeof decodePoint;
|
|
21
|
+
/** 单点定点解码。返回 null 表示该 PTS 无帧输出(空帧)。 */
|
|
22
|
+
export declare function decodePoint(videoPath: string, ptsMs: number, options: DecodePointOptions): Promise<RgbImage | null>;
|
|
23
|
+
export interface PtsIndexEntry {
|
|
24
|
+
frameIndex: number;
|
|
25
|
+
ptsMs: number;
|
|
26
|
+
}
|
|
27
|
+
/** 窗口帧 PTS 索引(ffprobe -show_frames,VFR 安全)。 */
|
|
28
|
+
export declare function probePtsIndex(ffprobePath: string, videoPath: string, window0Ms: number, window1Ms: number, signal?: AbortSignal | undefined): Promise<PtsIndexEntry[]>;
|
|
29
|
+
export interface RangeDecodeOptions extends DecodePointOptions {
|
|
30
|
+
workMaxEdge?: number | undefined;
|
|
31
|
+
/** 每次 ffmpeg 解码进程启动时回调(argv 快照),供单遍解码断言计数。 */
|
|
32
|
+
onFfmpegSpawn?: ((argv: string[]) => void) | undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 区间单遍解码(批量定点退化路径与流式共用骨架)。
|
|
36
|
+
* 返回帧序列(display 坐标系,全分辨率或受 workMaxEdge 上限的等比缩放)。
|
|
37
|
+
*/
|
|
38
|
+
export declare function decodeRange(videoPath: string, window0Ms: number, window1Ms: number, options: RangeDecodeOptions, onFrame?: (frame: RgbImage, ptsMs: number) => void | Promise<void>): Promise<Array<{
|
|
39
|
+
ptsMs: number;
|
|
40
|
+
image: RgbImage;
|
|
41
|
+
}>>;
|
|
42
|
+
export interface StreamDecodeResult {
|
|
43
|
+
frameCount: number;
|
|
44
|
+
/** 帧序号超出 PTS 索引长度时按最近 PTS 钳制的帧数。 */
|
|
45
|
+
realigned: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 单遍流式区间解码:stdout 逐帧切片回调,原始字节不整体驻留。
|
|
49
|
+
* onFrame 抛错时终止子进程并向上传播。
|
|
50
|
+
*/
|
|
51
|
+
export declare function decodeRangeStreaming(videoPath: string, window0Ms: number, window1Ms: number, options: RangeDecodeOptions, onFrame: (image: RgbImage, ptsMs: number) => Promise<void> | void): Promise<StreamDecodeResult>;
|
|
52
|
+
/**
|
|
53
|
+
* 空帧回退解码(契约 3):优先目标 PTS,空帧依次回退
|
|
54
|
+
* 调用方最后有效帧 → lastPtsMs → 半帧回退(PTS 毫秒取整进位会把
|
|
55
|
+
* -ss 目标推到末帧之后,回退 4ms 覆盖至 120fps 栅格),仍空则报错。
|
|
56
|
+
*/
|
|
57
|
+
export declare function decodePointWithFallback(videoPath: string, ptsMs: number, lastPtsMs: number, options: DecodePointOptions, lastValid?: RgbImage | null): Promise<RgbImage>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage 0 —— 双模解码调度(契约 3)。
|
|
3
|
+
*
|
|
4
|
+
* - seek_points(定点寻址):目标 PTS 排序去重后逐点 `ffmpeg -ss <pts> -i <file> -frames:v 1`
|
|
5
|
+
* 关键帧跳跃 + 精解码,全分辨率(保障 OCR 级细节);
|
|
6
|
+
* - 批量退化:目标数 ≥32 且平均间隔 ≤2s 时,排序后单遍区间解码摊薄寻址开销;
|
|
7
|
+
* - 空帧回退:解码端空输出时回退复用最后一个有效解码帧(契约 3);
|
|
8
|
+
* - 解码输出天然处于显示坐标系(ffmpeg 默认 autorotate)。
|
|
9
|
+
*
|
|
10
|
+
* decodeRangeStreaming 为单遍流式消费(契约 3:每帧解码后同步派生感知图,
|
|
11
|
+
* 当场裁剪缩放入队并释放原图;await onFrame 期间 stdout 异步迭代自动背压)。
|
|
12
|
+
*/
|
|
13
|
+
import { spawn } from 'node:child_process';
|
|
14
|
+
import { PfStreamError } from '../errors.js';
|
|
15
|
+
import { execFile } from './ffmpeg.js';
|
|
16
|
+
/** 单点定点解码。返回 null 表示该 PTS 无帧输出(空帧)。 */
|
|
17
|
+
export async function decodePoint(videoPath, ptsMs, options) {
|
|
18
|
+
const sec = (ptsMs / 1000).toFixed(3);
|
|
19
|
+
const argv = [
|
|
20
|
+
'-hide_banner',
|
|
21
|
+
'-loglevel', 'error',
|
|
22
|
+
'-ss', sec,
|
|
23
|
+
'-i', videoPath,
|
|
24
|
+
'-frames:v', '1',
|
|
25
|
+
'-an', '-sn', '-dn',
|
|
26
|
+
'-f', 'rawvideo',
|
|
27
|
+
'-pix_fmt', 'rgb24',
|
|
28
|
+
'pipe:1',
|
|
29
|
+
];
|
|
30
|
+
const { stdout } = await execFile(options.ffmpegPath, argv, {
|
|
31
|
+
signal: options.signal,
|
|
32
|
+
encoding: 'buffer',
|
|
33
|
+
});
|
|
34
|
+
const frameBytes = options.displayWidth * options.displayHeight * 3;
|
|
35
|
+
if (stdout.length === 0)
|
|
36
|
+
return null;
|
|
37
|
+
return { width: options.displayWidth, height: options.displayHeight, data: stdout.subarray(0, frameBytes) };
|
|
38
|
+
}
|
|
39
|
+
/** 窗口帧 PTS 索引(ffprobe -show_frames,VFR 安全)。 */
|
|
40
|
+
export async function probePtsIndex(ffprobePath, videoPath, window0Ms, window1Ms, signal) {
|
|
41
|
+
const interval = `${(window0Ms / 1000).toFixed(3)}%${(window1Ms / 1000).toFixed(3)}`;
|
|
42
|
+
const argv = [
|
|
43
|
+
'-v', 'error',
|
|
44
|
+
'-select_streams', 'v:0',
|
|
45
|
+
'-read_intervals', interval,
|
|
46
|
+
'-show_entries', 'frame=pts_time',
|
|
47
|
+
'-of', 'json',
|
|
48
|
+
videoPath,
|
|
49
|
+
];
|
|
50
|
+
const { stdout } = await execFile(ffprobePath, argv, { signal, encoding: 'utf8' });
|
|
51
|
+
const parsed = JSON.parse(stdout);
|
|
52
|
+
const entries = [];
|
|
53
|
+
for (const f of parsed.frames ?? []) {
|
|
54
|
+
const t = f.pts_time;
|
|
55
|
+
if (t == null)
|
|
56
|
+
continue;
|
|
57
|
+
const ms = Number.parseFloat(t) * 1000;
|
|
58
|
+
// read_intervals 起点为关键帧回卷(pre-roll):丢弃窗口外条目,
|
|
59
|
+
// 保证与 ffmpeg 精确 -ss 的首帧对齐(VFR 安全,亚毫秒精度)。
|
|
60
|
+
if (ms < window0Ms - 0.5 || ms > window1Ms + 0.5)
|
|
61
|
+
continue;
|
|
62
|
+
entries.push({ frameIndex: entries.length, ptsMs: ms });
|
|
63
|
+
}
|
|
64
|
+
return entries;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 区间单遍解码(批量定点退化路径与流式共用骨架)。
|
|
68
|
+
* 返回帧序列(display 坐标系,全分辨率或受 workMaxEdge 上限的等比缩放)。
|
|
69
|
+
*/
|
|
70
|
+
export async function decodeRange(videoPath, window0Ms, window1Ms, options, onFrame) {
|
|
71
|
+
const index = await probePtsIndexExec(options, videoPath, window0Ms, window1Ms);
|
|
72
|
+
if (index.length === 0)
|
|
73
|
+
return [];
|
|
74
|
+
const geometry = rangeOutputGeometry(options);
|
|
75
|
+
const argv = rangeArgv(videoPath, window0Ms, window1Ms, geometry);
|
|
76
|
+
const { stdout } = await execFile(options.ffmpegPath, argv, {
|
|
77
|
+
signal: options.signal,
|
|
78
|
+
encoding: 'buffer',
|
|
79
|
+
});
|
|
80
|
+
const frameBytes = geometry.outW * geometry.outH * 3;
|
|
81
|
+
const frameCount = Math.floor(stdout.length / frameBytes);
|
|
82
|
+
const frames = [];
|
|
83
|
+
for (let i = 0; i < frameCount; i++) {
|
|
84
|
+
const ptsMs = index[Math.min(i, index.length - 1)].ptsMs;
|
|
85
|
+
const image = {
|
|
86
|
+
width: geometry.outW,
|
|
87
|
+
height: geometry.outH,
|
|
88
|
+
data: stdout.subarray(i * frameBytes, (i + 1) * frameBytes),
|
|
89
|
+
};
|
|
90
|
+
frames.push({ ptsMs, image });
|
|
91
|
+
await onFrame?.(image, ptsMs);
|
|
92
|
+
}
|
|
93
|
+
return frames;
|
|
94
|
+
}
|
|
95
|
+
async function probePtsIndexExec(options, videoPath, w0, w1) {
|
|
96
|
+
// ffprobe 路径与 ffmpeg 同目录约定:入口适配层保证成对解析
|
|
97
|
+
const ffprobe = options.ffmpegPath.replace(/ffmpeg(\.exe)?$/i, 'ffprobe$1');
|
|
98
|
+
return probePtsIndex(ffprobe, videoPath, w0, w1, options.signal);
|
|
99
|
+
}
|
|
100
|
+
/** 区间解码输出几何(workMaxEdge 等比上限)。 */
|
|
101
|
+
function rangeOutputGeometry(options) {
|
|
102
|
+
const vf = [];
|
|
103
|
+
let outW = options.displayWidth;
|
|
104
|
+
let outH = options.displayHeight;
|
|
105
|
+
const maxEdge = options.workMaxEdge ?? 0;
|
|
106
|
+
if (maxEdge > 0 && Math.max(options.displayWidth, options.displayHeight) > maxEdge) {
|
|
107
|
+
const k = maxEdge / Math.max(options.displayWidth, options.displayHeight);
|
|
108
|
+
outW = Math.max(2, Math.round(options.displayWidth * k) & ~1);
|
|
109
|
+
outH = Math.max(2, Math.round(options.displayHeight * k) & ~1);
|
|
110
|
+
vf.push(`scale=${outW}:${outH}`);
|
|
111
|
+
}
|
|
112
|
+
return { outW, outH, vf };
|
|
113
|
+
}
|
|
114
|
+
function rangeArgv(videoPath, window0Ms, window1Ms, geometry) {
|
|
115
|
+
const durationSec = ((window1Ms - window0Ms) / 1000 + 1.0).toFixed(3);
|
|
116
|
+
return [
|
|
117
|
+
'-hide_banner',
|
|
118
|
+
'-loglevel', 'error',
|
|
119
|
+
'-ss', (window0Ms / 1000).toFixed(3),
|
|
120
|
+
'-i', videoPath,
|
|
121
|
+
'-t', durationSec,
|
|
122
|
+
'-an', '-sn', '-dn',
|
|
123
|
+
'-fps_mode', 'passthrough',
|
|
124
|
+
...(geometry.vf.length > 0 ? ['-vf', geometry.vf.join(',')] : []),
|
|
125
|
+
'-f', 'rawvideo',
|
|
126
|
+
'-pix_fmt', 'rgb24',
|
|
127
|
+
'pipe:1',
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 单遍流式区间解码:stdout 逐帧切片回调,原始字节不整体驻留。
|
|
132
|
+
* onFrame 抛错时终止子进程并向上传播。
|
|
133
|
+
*/
|
|
134
|
+
export async function decodeRangeStreaming(videoPath, window0Ms, window1Ms, options, onFrame) {
|
|
135
|
+
const index = await probePtsIndexExec(options, videoPath, window0Ms, window1Ms);
|
|
136
|
+
if (index.length === 0)
|
|
137
|
+
return { frameCount: 0, realigned: 0 };
|
|
138
|
+
const geometry = rangeOutputGeometry(options);
|
|
139
|
+
const argv = rangeArgv(videoPath, window0Ms, window1Ms, geometry);
|
|
140
|
+
const frameBytes = geometry.outW * geometry.outH * 3;
|
|
141
|
+
const child = spawn(options.ffmpegPath, argv, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
142
|
+
options.onFfmpegSpawn?.(argv);
|
|
143
|
+
const stderrChunks = [];
|
|
144
|
+
let stderrBytes = 0;
|
|
145
|
+
child.stderr?.on('data', (chunk) => {
|
|
146
|
+
if (stderrBytes < 64 * 1024) {
|
|
147
|
+
stderrChunks.push(chunk);
|
|
148
|
+
stderrBytes += chunk.length;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
let pending = null;
|
|
152
|
+
let frameIndex = 0;
|
|
153
|
+
let realigned = 0;
|
|
154
|
+
let killed = false;
|
|
155
|
+
let indexExhausted = false;
|
|
156
|
+
const exited = new Promise((resolveExit, rejectExit) => {
|
|
157
|
+
child.once('error', rejectExit);
|
|
158
|
+
child.once('close', (code) => resolveExit(code));
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
for await (const chunk of child.stdout) {
|
|
162
|
+
pending = pending ? Buffer.concat([pending, chunk]) : chunk;
|
|
163
|
+
while (pending && pending.length >= frameBytes) {
|
|
164
|
+
if (frameIndex >= index.length) {
|
|
165
|
+
// 索引耗尽:解码裕量产出的 surplus 帧位于窗口之外,丢弃并终止解码。
|
|
166
|
+
indexExhausted = true;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
const image = {
|
|
170
|
+
width: geometry.outW,
|
|
171
|
+
height: geometry.outH,
|
|
172
|
+
data: pending.subarray(0, frameBytes),
|
|
173
|
+
};
|
|
174
|
+
await onFrame(image, index[frameIndex].ptsMs);
|
|
175
|
+
pending = pending.length > frameBytes ? pending.subarray(frameBytes) : null;
|
|
176
|
+
frameIndex++;
|
|
177
|
+
}
|
|
178
|
+
if (indexExhausted) {
|
|
179
|
+
killed = true;
|
|
180
|
+
child.kill();
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
if (options.signal?.aborted)
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
killed = true;
|
|
189
|
+
child.kill();
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (options.signal?.aborted && !killed) {
|
|
193
|
+
child.kill();
|
|
194
|
+
await exited;
|
|
195
|
+
throw new PfStreamError('PFSTREAM_EXEC_ABORT', '解码已被调用方取消');
|
|
196
|
+
}
|
|
197
|
+
const code = await exited;
|
|
198
|
+
if (code !== 0 && !killed) {
|
|
199
|
+
const stderrTail = Buffer.concat(stderrChunks)
|
|
200
|
+
.subarray(Math.max(0, stderrBytes - 2048))
|
|
201
|
+
.toString('utf8');
|
|
202
|
+
throw new PfStreamError('PFSTREAM_EXEC_NONZERO', `ffmpeg 流式解码退出码 ${code}\nstderr 尾部:\n${stderrTail}`);
|
|
203
|
+
}
|
|
204
|
+
return { frameCount: frameIndex, realigned };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 空帧回退解码(契约 3):优先目标 PTS,空帧依次回退
|
|
208
|
+
* 调用方最后有效帧 → lastPtsMs → 半帧回退(PTS 毫秒取整进位会把
|
|
209
|
+
* -ss 目标推到末帧之后,回退 4ms 覆盖至 120fps 栅格),仍空则报错。
|
|
210
|
+
*/
|
|
211
|
+
export async function decodePointWithFallback(videoPath, ptsMs, lastPtsMs, options, lastValid) {
|
|
212
|
+
const direct = await decodePoint(videoPath, ptsMs, options);
|
|
213
|
+
if (direct)
|
|
214
|
+
return direct;
|
|
215
|
+
if (lastValid)
|
|
216
|
+
return lastValid;
|
|
217
|
+
if (ptsMs !== lastPtsMs) {
|
|
218
|
+
const fallback = await decodePoint(videoPath, lastPtsMs, options);
|
|
219
|
+
if (fallback)
|
|
220
|
+
return fallback;
|
|
221
|
+
}
|
|
222
|
+
if (ptsMs > 0) {
|
|
223
|
+
const nudge = await decodePoint(videoPath, ptsMs - 4, options);
|
|
224
|
+
if (nudge)
|
|
225
|
+
return nudge;
|
|
226
|
+
}
|
|
227
|
+
throw new PfStreamError('PFSTREAM_DECODE_EMPTY', `时间点 ${ptsMs}ms 无可解码帧`);
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=decoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decoder.js","sourceRoot":"","sources":["../../src/stage0/decoder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ffmpeg / ffprobe 子进程封装。
|
|
3
|
+
*
|
|
4
|
+
* 约束(技术方案 §4.7 交互协议):
|
|
5
|
+
* - argv 数组直接传给 spawn,不经 shell 拼接;
|
|
6
|
+
* - stdout / stderr 分别累计,超上限截断(防失控输出撑爆内存);
|
|
7
|
+
* - 超时与调用方 AbortSignal 协作终止进程,退出码非 0 时抛参数化错误;
|
|
8
|
+
* - Windows 沙箱下管道捕获受限时优先 spawn stdio:'ignore' 形态(由调用方选择)。
|
|
9
|
+
*/
|
|
10
|
+
export interface ExecOptions {
|
|
11
|
+
/** 毫秒;缺省不限时。 */
|
|
12
|
+
timeoutMs?: number | undefined;
|
|
13
|
+
/** 调用方取消信号。 */
|
|
14
|
+
signal?: AbortSignal | undefined;
|
|
15
|
+
/** stdout 编码:'utf8' 返回 string,'buffer' 返回 Buffer。 */
|
|
16
|
+
encoding?: 'utf8' | 'buffer' | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface ExecTextResult {
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
exitCode: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ExecBufferResult {
|
|
24
|
+
stdout: Buffer;
|
|
25
|
+
stderr: string;
|
|
26
|
+
exitCode: number;
|
|
27
|
+
}
|
|
28
|
+
export type ExecResult = ExecTextResult | ExecBufferResult;
|
|
29
|
+
export declare function execFile(file: string, args: string[], options: ExecOptions & {
|
|
30
|
+
encoding?: 'utf8';
|
|
31
|
+
}): Promise<ExecTextResult>;
|
|
32
|
+
export declare function execFile(file: string, args: string[], options: ExecOptions & {
|
|
33
|
+
encoding: 'buffer';
|
|
34
|
+
}): Promise<ExecBufferResult>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ffmpeg / ffprobe 子进程封装。
|
|
3
|
+
*
|
|
4
|
+
* 约束(技术方案 §4.7 交互协议):
|
|
5
|
+
* - argv 数组直接传给 spawn,不经 shell 拼接;
|
|
6
|
+
* - stdout / stderr 分别累计,超上限截断(防失控输出撑爆内存);
|
|
7
|
+
* - 超时与调用方 AbortSignal 协作终止进程,退出码非 0 时抛参数化错误;
|
|
8
|
+
* - Windows 沙箱下管道捕获受限时优先 spawn stdio:'ignore' 形态(由调用方选择)。
|
|
9
|
+
*/
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { PfStreamError } from '../errors.js';
|
|
12
|
+
const MAX_STDOUT_BYTES = 64 * 1024 * 1024;
|
|
13
|
+
const MAX_STDERR_BYTES = 256 * 1024;
|
|
14
|
+
export function execFile(file, args, options = {}) {
|
|
15
|
+
const { timeoutMs, signal, encoding = 'utf8' } = options;
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
let child;
|
|
18
|
+
try {
|
|
19
|
+
child = spawn(file, args, {
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
21
|
+
windowsHide: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
reject(new PfStreamError('PFSTREAM_SPAWN_FAILED', `无法启动 ${file}:${error.message}`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const stdoutChunks = [];
|
|
29
|
+
const stderrChunks = [];
|
|
30
|
+
let stdoutBytes = 0;
|
|
31
|
+
let stderrBytes = 0;
|
|
32
|
+
let settled = false;
|
|
33
|
+
const tail = (chunks, bytes) => Buffer.concat(chunks.length > 8 ? chunks.slice(-8) : chunks).subarray(Math.max(0, bytes - 2048)).toString('utf8');
|
|
34
|
+
const onKill = (code, message) => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
reject(new PfStreamError(code, `${message}\nstderr 尾部:\n${tail(stderrChunks, stderrBytes)}`));
|
|
39
|
+
};
|
|
40
|
+
child.stdout.on('data', (chunk) => {
|
|
41
|
+
if (stdoutBytes < MAX_STDOUT_BYTES) {
|
|
42
|
+
stdoutChunks.push(chunk);
|
|
43
|
+
stdoutBytes += chunk.length;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
child.stderr.on('data', (chunk) => {
|
|
47
|
+
if (stderrBytes < MAX_STDERR_BYTES) {
|
|
48
|
+
stderrChunks.push(chunk);
|
|
49
|
+
stderrBytes += chunk.length;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const timer = timeoutMs !== undefined
|
|
53
|
+
? setTimeout(() => {
|
|
54
|
+
child.kill();
|
|
55
|
+
onKill('PFSTREAM_EXEC_TIMEOUT', `${file} 执行超过 ${timeoutMs}ms`);
|
|
56
|
+
}, timeoutMs)
|
|
57
|
+
: undefined;
|
|
58
|
+
const onAbort = () => {
|
|
59
|
+
child.kill();
|
|
60
|
+
onKill('PFSTREAM_EXEC_ABORT', `${file} 被调用方取消`);
|
|
61
|
+
};
|
|
62
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
63
|
+
child.on('error', (error) => {
|
|
64
|
+
if (settled)
|
|
65
|
+
return;
|
|
66
|
+
settled = true;
|
|
67
|
+
if (timer !== undefined)
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
signal?.removeEventListener('abort', onAbort);
|
|
70
|
+
reject(new PfStreamError('PFSTREAM_SPAWN_FAILED', `无法启动 ${file}:${error.message}`));
|
|
71
|
+
});
|
|
72
|
+
child.on('close', (exitCode) => {
|
|
73
|
+
if (settled)
|
|
74
|
+
return;
|
|
75
|
+
settled = true;
|
|
76
|
+
if (timer !== undefined)
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
signal?.removeEventListener('abort', onAbort);
|
|
79
|
+
const stdout = encoding === 'buffer' ? Buffer.concat(stdoutChunks) : Buffer.concat(stdoutChunks).toString('utf8');
|
|
80
|
+
const stderr = Buffer.concat(stderrChunks).toString('utf8');
|
|
81
|
+
if (exitCode === 0) {
|
|
82
|
+
resolve({ stdout, stderr, exitCode });
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
reject(new PfStreamError('PFSTREAM_EXEC_NONZERO', `${file} 退出码 ${exitCode}\nstderr 尾部:\n${tail(stderrChunks, stderrBytes)}`));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=ffmpeg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../src/stage0/ffmpeg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage 0 媒体探测:元数据 + 旋转归一化 + 末帧 PTS 双路径(契约 2 / 契约 3)。
|
|
3
|
+
*
|
|
4
|
+
* - 旋转双源:side_data_list[].rotation(Display Matrix,ffprobe 归一化到
|
|
5
|
+
* [-90,180] 区间,如 270° 报告 -90)与旧式 tags.rotate;统一归一到
|
|
6
|
+
* {0,90,180,270},90°/270° 时在元数据层交换宽高建立显示坐标系。
|
|
7
|
+
* - last_pts_ms:优先 ffprobe 尾段逐帧探测(O(0.6s)),无帧输出时按
|
|
8
|
+
* duration_ms − 2×(1000/avg_fps) 保守估算(VFR 末帧间隔覆盖)。
|
|
9
|
+
*/
|
|
10
|
+
import type { ProbeResult } from '../types.js';
|
|
11
|
+
/** ffprobe -show_format -show_streams 的 JSON 形状(仅声明消费的字段)。 */
|
|
12
|
+
export interface FfprobeJson {
|
|
13
|
+
streams?: Array<{
|
|
14
|
+
index?: number;
|
|
15
|
+
codec_type?: string;
|
|
16
|
+
codec_name?: string;
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
avg_frame_rate?: string;
|
|
20
|
+
r_frame_rate?: string;
|
|
21
|
+
duration?: string;
|
|
22
|
+
channels?: number;
|
|
23
|
+
tags?: Record<string, string>;
|
|
24
|
+
side_data_list?: Array<{
|
|
25
|
+
side_data_type?: string;
|
|
26
|
+
rotation?: number;
|
|
27
|
+
}>;
|
|
28
|
+
}>;
|
|
29
|
+
format?: {
|
|
30
|
+
duration?: string;
|
|
31
|
+
tags?: Record<string, string>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface ProbeVideoOptions {
|
|
35
|
+
ffprobePath: string;
|
|
36
|
+
signal?: AbortSignal | undefined;
|
|
37
|
+
}
|
|
38
|
+
/** 旋转角归一化到 {0, 90, 180, 270};非法输入按 0 处理。 */
|
|
39
|
+
export declare function normalizeRotation(degrees: number | undefined | null): 0 | 90 | 180 | 270;
|
|
40
|
+
/** 双源解析旋转角:Display Matrix 优先,旧式 tags.rotate 次之。 */
|
|
41
|
+
export declare function extractRotation(stream: NonNullable<FfprobeJson['streams']>[number]): 0 | 90 | 180 | 270;
|
|
42
|
+
/**
|
|
43
|
+
* 由 ffprobe JSON 构造 ProbeResult(纯函数,供单测直接覆盖 tags.rotate 等合成输入)。
|
|
44
|
+
* duration 优先级:format.duration > 视频流 duration > format.tags.DURATION。
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildProbeResult(raw: FfprobeJson): ProbeResult;
|
|
47
|
+
export declare function probeVideo(videoPath: string, options: ProbeVideoOptions): Promise<ProbeResult>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage 0 媒体探测:元数据 + 旋转归一化 + 末帧 PTS 双路径(契约 2 / 契约 3)。
|
|
3
|
+
*
|
|
4
|
+
* - 旋转双源:side_data_list[].rotation(Display Matrix,ffprobe 归一化到
|
|
5
|
+
* [-90,180] 区间,如 270° 报告 -90)与旧式 tags.rotate;统一归一到
|
|
6
|
+
* {0,90,180,270},90°/270° 时在元数据层交换宽高建立显示坐标系。
|
|
7
|
+
* - last_pts_ms:优先 ffprobe 尾段逐帧探测(O(0.6s)),无帧输出时按
|
|
8
|
+
* duration_ms − 2×(1000/avg_fps) 保守估算(VFR 末帧间隔覆盖)。
|
|
9
|
+
*/
|
|
10
|
+
import { stat } from 'node:fs/promises';
|
|
11
|
+
import { PfStreamError } from '../errors.js';
|
|
12
|
+
import { execFile } from './ffmpeg.js';
|
|
13
|
+
import { clamp, estimateLastPtsMs, parseRational, secondsToMs } from '../utils/pts.js';
|
|
14
|
+
const PROBE_TIMEOUT_MS = 60_000;
|
|
15
|
+
/** 旋转角归一化到 {0, 90, 180, 270};非法输入按 0 处理。 */
|
|
16
|
+
export function normalizeRotation(degrees) {
|
|
17
|
+
if (typeof degrees !== 'number' || !Number.isFinite(degrees))
|
|
18
|
+
return 0;
|
|
19
|
+
const normalized = ((Math.round(degrees) % 360) + 360) % 360;
|
|
20
|
+
if (normalized === 90)
|
|
21
|
+
return 90;
|
|
22
|
+
if (normalized === 180)
|
|
23
|
+
return 180;
|
|
24
|
+
if (normalized === 270)
|
|
25
|
+
return 270;
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
/** 双源解析旋转角:Display Matrix 优先,旧式 tags.rotate 次之。 */
|
|
29
|
+
export function extractRotation(stream) {
|
|
30
|
+
const displayMatrix = (stream.side_data_list ?? []).find((side) => side.side_data_type === 'Display Matrix' && typeof side.rotation === 'number');
|
|
31
|
+
if (displayMatrix)
|
|
32
|
+
return normalizeRotation(displayMatrix.rotation);
|
|
33
|
+
const rotateTag = stream.tags?.['rotate'];
|
|
34
|
+
if (rotateTag !== undefined)
|
|
35
|
+
return normalizeRotation(Number.parseFloat(rotateTag));
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 由 ffprobe JSON 构造 ProbeResult(纯函数,供单测直接覆盖 tags.rotate 等合成输入)。
|
|
40
|
+
* duration 优先级:format.duration > 视频流 duration > format.tags.DURATION。
|
|
41
|
+
*/
|
|
42
|
+
export function buildProbeResult(raw) {
|
|
43
|
+
const video = raw.streams?.find((stream) => stream.codec_type === 'video');
|
|
44
|
+
if (!video) {
|
|
45
|
+
throw new PfStreamError('PFSTREAM_PROBE_NO_VIDEO', '容器内不存在视频流');
|
|
46
|
+
}
|
|
47
|
+
const codedWidth = video.width ?? 0;
|
|
48
|
+
const codedHeight = video.height ?? 0;
|
|
49
|
+
const rotation = extractRotation(video);
|
|
50
|
+
const swap = rotation === 90 || rotation === 270;
|
|
51
|
+
const avgFps = parseRational(video.avg_frame_rate) > 0
|
|
52
|
+
? parseRational(video.avg_frame_rate)
|
|
53
|
+
: parseRational(video.r_frame_rate);
|
|
54
|
+
const durationMs = secondsToMs(raw.format?.duration) ||
|
|
55
|
+
secondsToMs(video.duration) ||
|
|
56
|
+
secondsToMs(raw.format?.tags?.['DURATION']);
|
|
57
|
+
const audioStreams = (raw.streams ?? [])
|
|
58
|
+
.filter((stream) => stream.codec_type === 'audio')
|
|
59
|
+
.map((stream) => ({
|
|
60
|
+
index: stream.index ?? 0,
|
|
61
|
+
codec: stream.codec_name ?? 'unknown',
|
|
62
|
+
...(stream.channels !== undefined ? { channels: stream.channels } : {}),
|
|
63
|
+
}));
|
|
64
|
+
const subtitleStreams = (raw.streams ?? [])
|
|
65
|
+
.filter((stream) => stream.codec_type === 'subtitle')
|
|
66
|
+
.map((stream) => ({
|
|
67
|
+
index: stream.index ?? 0,
|
|
68
|
+
codec: stream.codec_name ?? 'unknown',
|
|
69
|
+
...(stream.tags?.['language'] !== undefined ? { language: stream.tags['language'] } : {}),
|
|
70
|
+
}));
|
|
71
|
+
return {
|
|
72
|
+
codedWidth,
|
|
73
|
+
codedHeight,
|
|
74
|
+
displayWidth: swap ? codedHeight : codedWidth,
|
|
75
|
+
displayHeight: swap ? codedWidth : codedHeight,
|
|
76
|
+
rotation,
|
|
77
|
+
durationMs,
|
|
78
|
+
lastPtsMs: estimateLastPtsMs(durationMs, avgFps),
|
|
79
|
+
avgFps,
|
|
80
|
+
codec: video.codec_name ?? 'unknown',
|
|
81
|
+
audioStreams,
|
|
82
|
+
subtitleStreams,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 末帧 PTS 逐帧探测:只读容器尾段(0.6s 起读窗口 + 5s 余量)。
|
|
87
|
+
* 返回末帧 pts_time 毫秒;无帧输出或探测失败返回 null(调用方回退估算路径)。
|
|
88
|
+
*/
|
|
89
|
+
async function probeLastFramePtsMs(options, durationMs, videoPath) {
|
|
90
|
+
const startSec = Math.max(0, (durationMs - 600) / 1000);
|
|
91
|
+
const argv = [
|
|
92
|
+
'-v', 'error',
|
|
93
|
+
'-select_streams', 'v:0',
|
|
94
|
+
'-show_frames',
|
|
95
|
+
'-show_entries', 'frame=pts_time',
|
|
96
|
+
'-of', 'json',
|
|
97
|
+
'-read_intervals', `${startSec.toFixed(3)}%+5`,
|
|
98
|
+
'-i', videoPath,
|
|
99
|
+
];
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execFile(options.ffprobePath, argv, {
|
|
103
|
+
timeoutMs: PROBE_TIMEOUT_MS,
|
|
104
|
+
signal: options.signal,
|
|
105
|
+
});
|
|
106
|
+
parsed = JSON.parse(stdout);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const frames = parsed.frames ?? [];
|
|
112
|
+
for (let i = frames.length - 1; i >= 0; i -= 1) {
|
|
113
|
+
const ptsTime = frames[i]?.pts_time;
|
|
114
|
+
if (ptsTime !== undefined) {
|
|
115
|
+
const ms = secondsToMs(ptsTime);
|
|
116
|
+
if (ms >= 0)
|
|
117
|
+
return clamp(ms, 0, Math.max(durationMs, ms));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
export async function probeVideo(videoPath, options) {
|
|
123
|
+
try {
|
|
124
|
+
const info = await stat(videoPath);
|
|
125
|
+
if (!info.isFile()) {
|
|
126
|
+
throw new PfStreamError('PFSTREAM_PROBE_NOT_FOUND', `路径不是常规文件:${videoPath}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (error instanceof PfStreamError)
|
|
131
|
+
throw error;
|
|
132
|
+
throw new PfStreamError('PFSTREAM_PROBE_NOT_FOUND', `文件不存在或不可访问:${videoPath}`);
|
|
133
|
+
}
|
|
134
|
+
let raw;
|
|
135
|
+
try {
|
|
136
|
+
const { stdout } = await execFile(options.ffprobePath, ['-v', 'error', '-print_format', 'json', '-show_format', '-show_streams', videoPath], { timeoutMs: PROBE_TIMEOUT_MS, signal: options.signal });
|
|
137
|
+
raw = JSON.parse(stdout);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (error instanceof PfStreamError && error.code === 'PFSTREAM_SPAWN_FAILED')
|
|
141
|
+
throw error;
|
|
142
|
+
throw new PfStreamError('PFSTREAM_PROBE_FAILED', error.message);
|
|
143
|
+
}
|
|
144
|
+
const result = buildProbeResult(raw);
|
|
145
|
+
const lastFramePtsMs = await probeLastFramePtsMs(options, result.durationMs, videoPath);
|
|
146
|
+
if (lastFramePtsMs !== null)
|
|
147
|
+
result.lastPtsMs = lastFramePtsMs;
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/stage0/probe.ts"],"names":[],"mappings":"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"}
|