mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
replay temp files instead of building from the rolling buffer
This commit is contained in:
@@ -75,6 +75,33 @@ function createReplayBuilder({ execFileAsync, fsp, ensureDir, renderSidebarVideo
|
|||||||
await execFileAsync(FFMPEG_BIN, ['-y','-hide_banner','-loglevel','error','-f','concat','-safe','0','-i',listPath,'-c','copy',outPath]);
|
await execFileAsync(FFMPEG_BIN, ['-y','-hide_banner','-loglevel','error','-f','concat','-safe','0','-i',listPath,'-c','copy',outPath]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function pinSegmentFiles(entries, tmpDir, prefix) {
|
||||||
|
/*
|
||||||
|
Replay segment files live in a rolling buffer, so cleanup can unlink one
|
||||||
|
while a slower replay build is still working. Pinning selected files into
|
||||||
|
the per-build temp directory gives ffmpeg stable paths for the whole build.
|
||||||
|
A hard link is preferred because it is cheap and keeps the inode alive even
|
||||||
|
when cleanup removes the original directory entry; copyFile is the fallback
|
||||||
|
for filesystems that do not support linking across the involved paths.
|
||||||
|
*/
|
||||||
|
const pinned = [];
|
||||||
|
for (let i = 0; i < entries.length; i += 1) {
|
||||||
|
const entry = entries[i];
|
||||||
|
const pinnedPath = path.join(tmpDir, `${prefix}-${String(i).padStart(4, '0')}.mp4`);
|
||||||
|
try {
|
||||||
|
await fsp.link(entry.filePath, pinnedPath);
|
||||||
|
} catch (linkErr) {
|
||||||
|
try {
|
||||||
|
await fsp.copyFile(entry.filePath, pinnedPath);
|
||||||
|
} catch (copyErr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pinned.push({ ...entry, filePath: pinnedPath });
|
||||||
|
}
|
||||||
|
return pinned;
|
||||||
|
}
|
||||||
|
|
||||||
async function probeMaxFrameSize(paths) {
|
async function probeMaxFrameSize(paths) {
|
||||||
let maxWidth = 0, maxHeight = 0;
|
let maxWidth = 0, maxHeight = 0;
|
||||||
for (const filePath of paths) {
|
for (const filePath of paths) {
|
||||||
@@ -108,7 +135,11 @@ function createReplayBuilder({ execFileAsync, fsp, ensureDir, renderSidebarVideo
|
|||||||
for (let i = 0; i < sources.length; i += 1) {
|
for (let i = 0; i < sources.length; i += 1) {
|
||||||
const source = sources[i];
|
const source = sources[i];
|
||||||
const sourceId = String(source.id);
|
const sourceId = String(source.id);
|
||||||
const videoEntries = overlapping(getVideoEntriesForSource({ type: String(source.type), id: sourceId }), tStart, tEnd);
|
const videoEntries = await pinSegmentFiles(
|
||||||
|
overlapping(getVideoEntriesForSource({ type: String(source.type), id: sourceId }), tStart, tEnd),
|
||||||
|
tmpDir,
|
||||||
|
`video-${i}-seg`,
|
||||||
|
);
|
||||||
if (!videoEntries.length) { missingSources.push({ ...source, reason: 'no video coverage in replay window' }); continue; }
|
if (!videoEntries.length) { missingSources.push({ ...source, reason: 'no video coverage in replay window' }); continue; }
|
||||||
|
|
||||||
const videoConcat = path.join(tmpDir, `video-${i}.mp4`);
|
const videoConcat = path.join(tmpDir, `video-${i}.mp4`);
|
||||||
@@ -122,7 +153,11 @@ function createReplayBuilder({ execFileAsync, fsp, ensureDir, renderSidebarVideo
|
|||||||
normalizedVideos.push({ path: videoTrimmed, source });
|
normalizedVideos.push({ path: videoTrimmed, source });
|
||||||
usedSources.push(source);
|
usedSources.push(source);
|
||||||
|
|
||||||
const audioEntries = overlapping(getAudioEntriesForSource(source), tStart, tEnd);
|
const audioEntries = await pinSegmentFiles(
|
||||||
|
overlapping(getAudioEntriesForSource(source), tStart, tEnd),
|
||||||
|
tmpDir,
|
||||||
|
`audio-${i}-seg`,
|
||||||
|
);
|
||||||
if (audioEntries.length) {
|
if (audioEntries.length) {
|
||||||
/*
|
/*
|
||||||
Audio workers are separate from selected video sources, even for PTZ
|
Audio workers are separate from selected video sources, even for PTZ
|
||||||
|
|||||||
Reference in New Issue
Block a user