视频保存
This commit is contained in:
@@ -515,6 +515,18 @@ export async function startRecording(input: StartRecordingInput): Promise<StartR
|
||||
const args = buildFfmpegArgs(sdpFilePath, outputPath);
|
||||
ffmpegProc = spawn(ffmpegBin, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||
|
||||
// ffmpeg stderr 环形缓冲:正常运行时 debug 级 + 不落盘,
|
||||
// 进程异常退出时把最后 N KB stderr 一次性以 error 级 dump 出来,方便定位 code=1 类问题
|
||||
// 上限 16 KB 足以覆盖典型 ffmpeg 启动失败诊断(codec/端口/SDP 解析等),且容量可控
|
||||
const STDERR_TAIL_LIMIT = 16 * 1024;
|
||||
let stderrTail = '';
|
||||
const appendStderrTail = (text: string) => {
|
||||
stderrTail += text;
|
||||
if (stderrTail.length > STDERR_TAIL_LIMIT) {
|
||||
stderrTail = stderrTail.slice(stderrTail.length - STDERR_TAIL_LIMIT);
|
||||
}
|
||||
};
|
||||
|
||||
const exited = new Promise<void>((resolve) => {
|
||||
ffmpegProc!.once('exit', (code, signal) => {
|
||||
const entry = recordingMap.get(id);
|
||||
@@ -522,8 +534,9 @@ export async function startRecording(input: StartRecordingInput): Promise<StartR
|
||||
entry.exitCode = code;
|
||||
if (!entry.stopping && code !== 0) {
|
||||
entry.failureReason = `ffmpeg exited unexpectedly code=${code} signal=${signal ?? ''}`;
|
||||
// 异常退出:把 stderr tail 落 error 级,便于线上排查 ffmpeg 失败根因
|
||||
log.error(
|
||||
{ recordingId: id, code, signal },
|
||||
{ recordingId: id, code, signal, stderrTail: stderrTail.trim() },
|
||||
'ffmpeg exited unexpectedly before stop',
|
||||
);
|
||||
// Phase B watchdog:异步触发失败回调(webhook + 用户回调),不阻塞 exit 处理
|
||||
@@ -533,7 +546,7 @@ export async function startRecording(input: StartRecordingInput): Promise<StartR
|
||||
// entry 还没入 map(startRecording 末尾才插入)→ 走 spawn 失败兜底,避免 webhook 漏报
|
||||
// startRecording 同步路径会自己 throw + rollback,这里上报只为防止上层 DB 行已存在的边缘场景
|
||||
log.error(
|
||||
{ recordingId: id, code, signal },
|
||||
{ recordingId: id, code, signal, stderrTail: stderrTail.trim() },
|
||||
'ffmpeg exited before recording entry registered',
|
||||
);
|
||||
fireSpawnFailure({
|
||||
@@ -541,7 +554,7 @@ export async function startRecording(input: StartRecordingInput): Promise<StartR
|
||||
roomCode: input.roomCode,
|
||||
routerId: input.routerId,
|
||||
exitCode: code,
|
||||
failureReason: `ffmpeg exited during spawn code=${code} signal=${signal ?? ''}`,
|
||||
failureReason: `ffmpeg exited during spawn code=${code} signal=${signal ?? ''}; stderr_tail=${stderrTail.trim().slice(-512)}`,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
@@ -550,7 +563,9 @@ export async function startRecording(input: StartRecordingInput): Promise<StartR
|
||||
|
||||
ffmpegProc.stderr?.on('data', (chunk: Buffer) => {
|
||||
const text = chunk.toString('utf-8');
|
||||
// ffmpeg 默认大部分日志走 stderr;只在 debug 级别落,避免日志爆炸
|
||||
// 1) 永远写入环形缓冲,供进程异常退出时 dump
|
||||
appendStderrTail(text);
|
||||
// 2) 正常实时日志仍走 debug 级,避免高频日志爆炸
|
||||
log.debug({ recordingId: id }, `[ffmpeg] ${text.trim()}`);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user