修复ffmpeg转码的错误提示,改为等待MP4文件生成完成后再播放。
This commit is contained in:
@@ -62,7 +62,7 @@
|
|||||||
<div id="transcoding-overlay" class="player-overlay hidden">
|
<div id="transcoding-overlay" class="player-overlay hidden">
|
||||||
<div class="spinner"></div>
|
<div class="spinner"></div>
|
||||||
<h3>Transcoding via FFmpeg...</h3>
|
<h3>Transcoding via FFmpeg...</h3>
|
||||||
<p>Generating HLS segments, please wait.</p>
|
<p>Generating MP4 file, please wait.</p>
|
||||||
<div id="progress-info" class="progress-info hidden">
|
<div id="progress-info" class="progress-info hidden">
|
||||||
<div id="progress-text" class="progress-text">Initializing...</div>
|
<div id="progress-text" class="progress-text">Initializing...</div>
|
||||||
<div class="progress-bar">
|
<div class="progress-bar">
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
if (message.type === 'ready' && message.key === currentVideoKey) {
|
if (message.type === 'ready' && message.key === currentVideoKey) {
|
||||||
setProgress({ status: 'Ready to play', percent: 100, details: 'Ready to play' });
|
setProgress({ status: 'Ready to play', percent: 100, details: 'Ready to play' });
|
||||||
playHlsStream(message.hlsUrl);
|
playMp4Stream(message.mp4Url);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('WebSocket message parse error:', error);
|
console.error('WebSocket message parse error:', error);
|
||||||
@@ -246,7 +246,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (data.error) throw new Error(data.error);
|
if (data.error) throw new Error(data.error);
|
||||||
|
|
||||||
if (!wsConnected) {
|
if (!wsConnected) {
|
||||||
pollForHlsReady(key, data.mp4Url);
|
pollForMp4Ready(key, data.mp4Url);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -255,7 +255,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Poll the backend to check if the generated MP4 file is accessible
|
// Poll the backend to check if the generated MP4 file is accessible
|
||||||
const pollForHlsReady = (key, hlsUrl) => {
|
const pollForMp4Ready = (key, mp4Url) => {
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = 120; // 60 seconds max wait for first segment
|
const maxAttempts = 120; // 60 seconds max wait for first segment
|
||||||
const pollIntervalMs = 500;
|
const pollIntervalMs = 500;
|
||||||
@@ -273,10 +273,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (data.ready) {
|
if (data.ready) {
|
||||||
stopPolling();
|
stopPolling();
|
||||||
setProgress({ status: 'Ready to play', percent: 100, details: 'Ready to play' });
|
setProgress({ status: 'Ready to play', percent: 100, details: 'Ready to play' });
|
||||||
playHlsStream(data.hlsUrl);
|
playMp4Stream(data.mp4Url);
|
||||||
} else if (attempts >= maxAttempts) {
|
} else if (attempts >= maxAttempts) {
|
||||||
stopPolling();
|
stopPolling();
|
||||||
transcodingOverlay.innerHTML = `<p style="color: #ef4444;">Timeout waiting for HLS segments.</p>`;
|
transcodingOverlay.innerHTML = `<p style="color: #ef4444;">Timeout waiting for MP4 file.</p>`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Poll Error:", err);
|
console.error("Poll Error:", err);
|
||||||
@@ -292,7 +292,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Initialize MP4 Player
|
// Initialize MP4 Player
|
||||||
const playHlsStream = (url) => {
|
const playMp4Stream = (url) => {
|
||||||
transcodingOverlay.classList.add('hidden');
|
transcodingOverlay.classList.add('hidden');
|
||||||
videoPlayer.classList.remove('hidden');
|
videoPlayer.classList.remove('hidden');
|
||||||
playBtn.classList.add('hidden');
|
playBtn.classList.add('hidden');
|
||||||
|
|||||||
23
server.js
23
server.js
@@ -200,12 +200,27 @@ app.post('/api/transcode', async (req, res) => {
|
|||||||
progressMap[progressKey] = progressState;
|
progressMap[progressKey] = progressState;
|
||||||
broadcastWs(progressKey, { type: 'progress', key: progressKey, progress: progressState });
|
broadcastWs(progressKey, { type: 'progress', key: progressKey, progress: progressState });
|
||||||
})
|
})
|
||||||
|
.on('stderr', (stderrLine) => {
|
||||||
|
console.log(`ffmpeg stderr: ${stderrLine}`);
|
||||||
|
})
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
console.log(`Finished transcoding ${key} to MP4`);
|
console.log(`Finished transcoding ${key} to MP4`);
|
||||||
const progressState = { status: 'finished', percent: 100, details: 'Transcoding complete', mp4Url };
|
let progressState;
|
||||||
|
try {
|
||||||
|
const stats = fs.statSync(mp4Path);
|
||||||
|
if (!stats.isFile() || stats.size === 0) {
|
||||||
|
throw new Error('Output MP4 is empty or missing');
|
||||||
|
}
|
||||||
|
progressState = { status: 'finished', percent: 100, details: 'Transcoding complete', mp4Url };
|
||||||
|
} catch (verifyError) {
|
||||||
|
console.error(`Output verification failed for ${mp4Path}:`, verifyError);
|
||||||
|
progressState = { status: 'failed', percent: progressMap[progressKey]?.percent || 0, details: `Output verification failed: ${verifyError.message}`, mp4Url };
|
||||||
|
}
|
||||||
progressMap[progressKey] = progressState;
|
progressMap[progressKey] = progressState;
|
||||||
broadcastWs(progressKey, { type: 'progress', key: progressKey, progress: progressState });
|
broadcastWs(progressKey, { type: 'progress', key: progressKey, progress: progressState });
|
||||||
broadcastWs(progressKey, { type: 'ready', key: progressKey, mp4Url });
|
if (progressState.status === 'finished') {
|
||||||
|
broadcastWs(progressKey, { type: 'ready', key: progressKey, mp4Url });
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.on('error', (err) => {
|
.on('error', (err) => {
|
||||||
console.error(`Error transcoding ${key}:`, err);
|
console.error(`Error transcoding ${key}:`, err);
|
||||||
@@ -234,8 +249,8 @@ app.get('/api/status', (req, res) => {
|
|||||||
const mp4Path = path.join(__dirname, 'public', 'mp4', ...safeKeySegments, 'video.mp4');
|
const mp4Path = path.join(__dirname, 'public', 'mp4', ...safeKeySegments, 'video.mp4');
|
||||||
const progress = progressMap[progressKey] || null;
|
const progress = progressMap[progressKey] || null;
|
||||||
|
|
||||||
// Check if the MP4 file exists
|
const outputReady = fs.existsSync(mp4Path) && (!progress || progress.status !== 'failed');
|
||||||
if (fs.existsSync(mp4Path)) {
|
if (outputReady) {
|
||||||
res.json({ ready: true, mp4Url: `/mp4/${safeKeySegments.join('/')}/video.mp4`, progress });
|
res.json({ ready: true, mp4Url: `/mp4/${safeKeySegments.join('/')}/video.mp4`, progress });
|
||||||
} else {
|
} else {
|
||||||
res.json({ ready: false, progress });
|
res.json({ ready: false, progress });
|
||||||
|
|||||||
Reference in New Issue
Block a user