尝试修复编码器的问题

This commit is contained in:
2026-04-10 01:20:56 +08:00
parent cc40f1920c
commit 72900de4ed

View File

@@ -933,11 +933,12 @@ const startHlsTranscode = async (bucket, key, seg, requestedEncoder, requestedDe
console.log(`[HLS] FFmpeg config: encoder=${encoderName}, decoder=${decoderName}, startTime=${startTime}s, subtitleIndex=${subtitleIndex}, persistent=${isPersistent}`); console.log(`[HLS] FFmpeg config: encoder=${encoderName}, decoder=${decoderName}, startTime=${startTime}s, subtitleIndex=${subtitleIndex}, persistent=${isPersistent}`);
const m3u8Name = isPersistent ? 'temp.m3u8' : `temp_${progressKey.replace(/[^a-zA-Z0-9]/g, '_')}.m3u8`; const m3u8Name = isPersistent ? 'temp.m3u8' : `temp_${progressKey.replace(/[^a-zA-Z0-9]/g, '_')}.m3u8`;
const m3u8Path = path.join(hlsDir, m3u8Name); const m3u8Path = path.join(hlsDir, m3u8Name).replace(/\\/g, '/');
if (fs.existsSync(m3u8Path)) fs.unlinkSync(m3u8Path); if (fs.existsSync(m3u8Path)) try { fs.unlinkSync(m3u8Path); } catch (e) {}
const normalizedInputPath = tmpInputPath.replace(/\\/g, '/');
const ffmpegCommand = ffmpeg().input(normalizedInputPath);
const ffmpegCommand = ffmpeg().input(tmpInputPath);
if (startTime > 0) ffmpegCommand.seekInput(startTime); if (startTime > 0) ffmpegCommand.seekInput(startTime);
ffmpegCommand.videoCodec(encoderName).audioCodec('aac'); ffmpegCommand.videoCodec(encoderName).audioCodec('aac');
@@ -1008,9 +1009,16 @@ const startHlsTranscode = async (bucket, key, seg, requestedEncoder, requestedDe
progressMap[broadcastRoomId] = failedState; progressMap[broadcastRoomId] = failedState;
broadcastWs(broadcastRoomId, { type: 'progress', key: broadcastKey, progress: failedState }); broadcastWs(broadcastRoomId, { type: 'progress', key: broadcastKey, progress: failedState });
hlsProcesses.delete(progressKey); hlsProcesses.delete(progressKey);
// Clean up temporary m3u8 file
const m3u8Name = isPersistent ? 'temp.m3u8' : `temp_${progressKey.replace(/[^a-zA-Z0-9]/g, '_')}.m3u8`;
const m3u8Path = path.join(hlsDir, m3u8Name);
if (!isPersistent && fs.existsSync(m3u8Path)) {
try { fs.unlinkSync(m3u8Path); } catch (e) {}
}
}); });
ffmpegCommand.on('progress', (progress) => { ffmpegCommand.on('progress', (progress) => {
const timemarkSeconds = parseTimemarkToSeconds(progress.timemark || '0'); const timemarkSeconds = parseTimemarkToSeconds(progress.timemark || '0');
const absoluteSeconds = startTime + (isFinite(timemarkSeconds) ? timemarkSeconds : 0); const absoluteSeconds = startTime + (isFinite(timemarkSeconds) ? timemarkSeconds : 0);
@@ -1066,8 +1074,13 @@ const startHlsTranscode = async (bucket, key, seg, requestedEncoder, requestedDe
progress: finishedState progress: finishedState
}); });
hlsProcesses.delete(progressKey); hlsProcesses.delete(progressKey);
// Clean up temporary m3u8 file
if (!isPersistent && fs.existsSync(m3u8Path)) {
try { fs.unlinkSync(m3u8Path); } catch (e) {}
}
}); });
ffmpegCommand.run(); ffmpegCommand.run();
console.log(`[HLS] FFmpeg process started for ${progressKey} (persistent=${isPersistent}, m3u8=${m3u8Name})`); console.log(`[HLS] FFmpeg process started for ${progressKey} (persistent=${isPersistent}, m3u8=${m3u8Name})`);
const newProcessInfo = { command: ffmpegCommand, currentSeg: seg, lastActive: Date.now(), persistent: isPersistent, m3u8Name }; const newProcessInfo = { command: ffmpegCommand, currentSeg: seg, lastActive: Date.now(), persistent: isPersistent, m3u8Name };
@@ -1128,43 +1141,68 @@ app.get('/api/hls/segment.ts', async (req, res) => {
const baseProgressKey = getProgressKey(key); const baseProgressKey = getProgressKey(key);
const subtitleSuffix = (subtitleIndex !== null && subtitleIndex !== undefined && subtitleIndex !== '-1') ? `-sub${subtitleIndex}` : ''; const subtitleSuffix = (subtitleIndex !== null && subtitleIndex !== undefined && subtitleIndex !== '-1') ? `-sub${subtitleIndex}` : '';
const progressKey = `${baseProgressKey}${subtitleSuffix}`; const progressKeyPrefix = `${baseProgressKey}${subtitleSuffix}`;
const hlsDir = getHlsCacheDir(bucket, key, subtitleIndex); const hlsDir = getHlsCacheDir(bucket, key, subtitleIndex);
if (!fs.existsSync(hlsDir)) fs.mkdirSync(hlsDir, { recursive: true }); if (!fs.existsSync(hlsDir)) fs.mkdirSync(hlsDir, { recursive: true });
const targetSegPath = path.join(hlsDir, `segment_${seg}.ts`); const targetSegPath = path.join(hlsDir, `segment_${seg}.ts`);
let currentProcess = hlsProcesses.get(progressKey);
// Find a process that is "closest" to our segment to avoid redundant side-processes
let currentProcess = null;
let closestKey = null;
for (const [procKey, procInfo] of hlsProcesses.entries()) {
if (procKey === progressKeyPrefix || procKey.startsWith(`${progressKeyPrefix}:`)) {
// Re-use if it's within a window of 2 to 10 segments ahead
if (seg >= (procInfo.currentSeg || 0) && seg <= (procInfo.currentSeg || 0) + 12) {
currentProcess = procInfo;
closestKey = procKey;
break;
}
}
}
if (currentProcess) { if (currentProcess) {
currentProcess.lastActive = Date.now(); currentProcess.lastActive = Date.now();
} }
const checkIsCachedAndCompleted = () => { const checkIsCachedAndCompleted = () => {
if (!fs.existsSync(targetSegPath)) return false; if (!fs.existsSync(targetSegPath)) return false;
const m3u8Path = path.join(hlsDir, `temp.m3u8`);
if (fs.existsSync(m3u8Path) && fs.readFileSync(m3u8Path, 'utf8').includes(`segment_${seg}.ts`)) return true; // Check all m3u8 files in the directory to see if any process has finished this segment
const files = fs.readdirSync(hlsDir);
for (const file of files) {
if (file.endsWith('.m3u8')) {
const m3u8Content = fs.readFileSync(path.join(hlsDir, file), 'utf8');
if (m3u8Content.includes(`segment_${seg}.ts`)) return true;
}
}
if (currentProcess && Math.abs((currentProcess.currentSeg || 0) - seg) > 3) return true; if (currentProcess && Math.abs((currentProcess.currentSeg || 0) - seg) > 3) return true;
// If there's no active process, any existing file is from a past complete run // If there's no active process, any existing file is from a past complete run or already verified
if (!currentProcess) return true; if (!currentProcess) return true;
return false; return false;
}; };
if (checkIsCachedAndCompleted()) { if (checkIsCachedAndCompleted()) {
if (currentProcess) currentProcess.currentSeg = Math.max(currentProcess.currentSeg, seg); if (currentProcess) currentProcess.currentSeg = Math.max(currentProcess.currentSeg, seg);
res.setHeader('Content-Type', 'video/MP2T'); res.setHeader('Content-Type', 'video/MP2T');
return res.sendFile(targetSegPath); return res.sendFile(targetSegPath);
} }
const needsNewProcess = !currentProcess || (!fs.existsSync(targetSegPath) && (seg < (currentProcess.currentSeg || 0) || seg > (currentProcess.currentSeg || 0) + 4)); // If we have no process or the closest one is too far, start a new one (only if we don't already have the segment)
const needsNewProcess = !currentProcess && !checkIsCachedAndCompleted();
if (needsNewProcess) { if (needsNewProcess) {
console.log(`[HLS] Starting new FFmpeg process for seg=${seg}, key=${key}`); console.log(`[HLS] Starting new FFmpeg side-process for seg=${seg}, key=${key}`);
currentProcess = await startHlsTranscode(bucket, key, seg, requestedEncoder, requestedDecoder, subtitleIndex); currentProcess = await startHlsTranscode(bucket, key, seg, requestedEncoder, requestedDecoder, subtitleIndex);
} else { } else if (currentProcess) {
console.log(`[HLS] Reusing existing FFmpeg process for seg=${seg}, currentSeg=${currentProcess?.currentSeg}`); console.log(`[HLS] Reusing existing FFmpeg process (${closestKey}) for seg=${seg}, currently at ${currentProcess.currentSeg}`);
} }
const ready = await waitForSegment(hlsDir, seg); const ready = await waitForSegment(hlsDir, seg);
if (!ready) { if (!ready) {
console.error(`[HLS] Segment generation timeout: seg=${seg}, key=${key}`); console.error(`[HLS] Segment generation timeout: seg=${seg}, key=${key}`);