Next.js集成ffmpeg-wasm时随机出现「内存访问越界」运行时错误的问题求助
Next.js集成ffmpeg-wasm时随机出现「内存访问越界」运行时错误的问题求助
各位开发者好,我最近在Next.js项目里用ffmpeg-wasm做图片格式转换,遇到了一个特别诡异的随机错误,卡壳好几天了,想请教下有没有人碰到过类似情况或者有排查思路。
问题详情
我做了个简单的页面:通过文件选择框上传图片,点击按钮后用ffmpeg把每张图转成JPEG格式,核心执行命令是:
await ffmpeg.exec(['-i', originalFileName, '-q:v', '5', convertedFileName]);
但每次操作都有大概50%的概率成功,50%的概率直接抛出**「memory access out of bounds」**的运行时错误,完全没规律可言。
我已经做了各种测试,结果都一样:
- 换过不同格式、大小的图片,甚至用纯色小图测试
- 重启应用、重启电脑,换Chrome/Firefox/Edge,连移动端浏览器也试过
- 换输出格式(比如转PNG、WebP),甚至换ffmpeg命令做其他操作(比如查询媒体信息)
- 确认过虚拟文件系统(VFS)的文件写入是正常的,原始图片能正确写入VFS
环境信息
- 系统:Windows 10
- Next.js版本:15.3.5(客户端组件,带
'use client'指令) - ffmpeg相关依赖:
@ffmpeg/ffmpeg@0.12.10,@ffmpeg/util对应版本
完整组件代码
'use client'; import * as React from 'react'; import { FFmpeg } from '@ffmpeg/ffmpeg'; import { fetchFile, toBlobURL } from '@ffmpeg/util'; export default function Page() { const [files, setFiles] = React.useState<File[]>([]); const [error, setError] = React.useState<string | null>(null); // FFmpeg 相关状态与引用 const ffmpegRef = React.useRef<FFmpeg | null>(null); const [ffmpegLoaded, setFFmpegLoaded] = React.useState(false); const [isConvertingImages, setIsConvertingImages] = React.useState(false); const [videoGenerationProgress, setVideoGenerationProgress] = React.useState<string>(''); // 组件挂载时加载FFmpeg React.useEffect(() => { const loadFFmpeg = async () => { const baseURL = "https://unpkg.com/@ffmpeg/core@0.12.10/dist/umd"; try { const ffmpeg = new FFmpeg(); // 监听FFmpeg日志 ffmpeg.on('log', ({ message }) => { if (message.includes('frame=')) { setVideoGenerationProgress(message); } }); await ffmpeg.load({ coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"), wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"), }); ffmpegRef.current = ffmpeg; setFFmpegLoaded(true); console.log('FFmpeg loaded successfully!'); } catch (err) { console.error('Failed to load FFmpeg:', err); setError('Failed to load video processing tools.'); } }; loadFFmpeg(); }, []); // 文件选择处理 const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { if (e.target.files && e.target.files.length > 0) { const newFiles = Array.from(e.target.files).filter(file => file.type.startsWith('image/')); setFiles(newFiles); setError(null); } }; // 图片转换逻辑 const handleConvertImages = async () => { if (!ffmpegLoaded || !ffmpegRef.current) { setError('FFmpeg is not loaded yet. Please wait.'); return; } if (files.length === 0) { setError('Please select images first to generate a video.'); return; } setIsConvertingImages(true); setError(null); setVideoGenerationProgress(''); try { const ffmpeg = ffmpegRef.current; const targetExtension = 'jpeg'; const convertedImageNames: string[] = []; for (let i = 0; i < files.length; i++) { const file = files[i]; const originalFileName = `original_image_${String(i).padStart(3, '0')}.${file.name.split('.').pop()}`; const convertedFileName = `converted_image_${String(i).padStart(3, '0')}.${targetExtension}`; convertedImageNames.push(convertedFileName); // 写入文件到VFS await ffmpeg.writeFile(`${originalFileName}`, await fetchFile(file)); console.log(`Wrote original ${originalFileName} to FFmpeg FS`); setVideoGenerationProgress(`Converting ${file.name} to ${targetExtension.toUpperCase()}...`); // 执行转换命令(随机出错的核心代码) await ffmpeg.exec(['-i', originalFileName, '-q:v', '5', convertedFileName]); console.log(`Converted ${originalFileName} to ${convertedFileName}`); // 删除原始文件释放内存 await ffmpeg.deleteFile(originalFileName); console.log(`Deleted original ${originalFileName} from FFmpeg FS`); } setFiles([]); setVideoGenerationProgress(`All images converted to ${targetExtension.toUpperCase()}.`); } catch (err) { console.error('Error converting images:', err); setError(`Failed to convert images: ${err instanceof Error ? err.message : String(err)}`); } finally { setIsConvertingImages(false); } }; return ( <div className="min-h-screen bg-gray-100 flex items-center justify-center p-4 relative overflow-hidden"> <div className="bg-white p-8 rounded-lg shadow-xl w-full max-w-2xl"> <h1 className="text-2xl font-bold mb-6">Image Converter</h1> <input type="file" multiple accept="image/*" onChange={handleFileChange} disabled={isConvertingImages} className="mb-4 block w-full p-2 border rounded" /> {error && <p className="text-red-500 mb-4">{error}</p>} <button onClick={handleConvertImages} disabled={!ffmpegLoaded || isConvertingImages || files.length === 0} className="px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed" > {isConvertingImages ? 'Converting...' : 'Convert Images'} </button> {videoGenerationProgress && <p className="mt-4 text-gray-600">{videoGenerationProgress}</p>} </div> </div> ); }
我现在怀疑是不是WebAssembly的内存管理和Next.js客户端组件的生命周期有冲突?或者ffmpeg-wasm在Windows环境下有什么没被发现的随机内存问题?有没有人有类似的排查经验,或者能给点方向参考?
内容来源于stack exchange
相关产品推荐
相关产品推荐

