使用临时文件路径作为输出时ffmpeg执行失败的问题排查
问题:使用fluent-ffmpeg结合tmp库生成临时缩略图失败
我在Node.js中使用fluent-ffmpeg库自动生成指定视频中间位置的单张缩略图,代码如下:
const screenshot = async (pathToFile: string) => { // 生成工作目录外带.jpg后缀的临时文件路径 const tempFileName = tmp.tmpNameSync({ postfix: ".jpg" }); try{ await new Promise((resolve, reject) => { ffmpeg(pathToFile) .thumbnail({ // 不使用tmpNameSync时可正常工作 filename: tempFileName, count: 1, timestamps: ["50%"] }) .on("end", resolve) .on("error", reject); }); } catch(err){ console.log(err); return null; } return tempFileName; };
当使用/path/to/thumbnail.jpg这类常规输出路径时代码运行正常,但用tmp库生成工作目录外的临时文件名时,ffmpeg抛出如下错误:
Error: ffmpeg exited with code 1: av_interleaved_write_frame(): Input/output error frame= 1 fps=0.0 q=7.8 size=N/A time=00:00:00.04 bitrate=N/A speed=0.152x frame= 1 fps=0.0 q=7.8 Lsize=N/A time=00:00:00.04 bitrate=N/A speed=0.141x video:119kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown Conversion failed!
直接在终端执行fluent-ffmpeg生成的命令可以正常运行,排除权限问题,但不确定操作方式是否有误。生成的完整ffmpeg命令(简化文件名)如下:
ffmpeg -ss 14.118271 -i /var/folders/__/XYZ/T/tmp-XYZ/tmp-XYZ -y -filter_complex scale=w=trunc(oh*a/2)*2:h=720[size0];[size0]split=1[screen0] -vframes 1 -map [screen0] var/folders/__/XYZ/T/tmp-XYZ.jpg
解决方案
观察生成的ffmpeg命令可以发现,输出路径var/folders/__/XYZ/T/tmp-XYZ.jpg缺少了开头的/,变成了相对路径,导致ffmpeg无法找到正确的输出目录,从而抛出输入输出错误。下面提供两种解决方式:
- 确保输出路径为绝对路径:在传递给
filename参数前,用path.resolve()包裹临时路径,强制转换为标准绝对路径,避免fluent-ffmpeg解析路径时丢失开头的斜杠。
修改后的代码:
import * as path from 'path'; const screenshot = async (pathToFile: string) => { const tempFileName = tmp.tmpNameSync({ postfix: ".jpg" }); const absoluteOutputPath = path.resolve(tempFileName); try{ await new Promise((resolve, reject) => { ffmpeg(pathToFile) .thumbnail({ filename: absoluteOutputPath, count: 1, timestamps: ["50%"] }) .on("end", resolve) .on("error", reject); }); } catch(err){ console.log(err); return null; } return tempFileName; };
- 使用临时目录+固定文件名:先通过tmp库创建临时目录,再在目录内指定固定文件名生成缩略图,这种方式路径结构更清晰,也能避免路径解析异常:
import * as path from 'path'; const screenshot = async (pathToFile: string) => { const tempDir = tmp.dirSync().name; const tempFileName = path.join(tempDir, 'thumbnail.jpg'); try{ await new Promise((resolve, reject) => { ffmpeg(pathToFile) .thumbnail({ filename: tempFileName, count: 1, timestamps: ["50%"] }) .on("end", resolve) .on("error", reject); }); } catch(err){ console.log(err); tmp.dirSync({ unsafeCleanup: true, dir: tempDir }); return null; } return tempFileName; };
这两种方式都能让ffmpeg获取到正确的输出路径,解决报错问题。
内容的提问来源于stack exchange,提问作者Grover
相关产品推荐
相关产品推荐

