昨天在调试 n8n 工作流时,遇到了一个 FFmpeg 视频截取的错误,折腾了好一阵子才搞定。
今天就把这个问题分享出来,希望能帮到遇到类似情况的朋友。
错误现场
先看看报错信息:
{ "errorMessage": "Command failed: VIDEO_FILE=\"/mnt/d/Downloads/movie/a45video/XF1327/renwu/\"", "n8nDetails": { "nodeName": "Execute Command", "nodeType": "n8n-nodes-base.executeCommand", "time": "2025/11/11 11:36:25" }}脚本的逻辑是这样的:
VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/"CLIP_DURATION_FLOAT=""
# 获取视频长度DURATION=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$VIDEO_FILE" 2>/dev/null)
# ...中间的计算逻辑...
# 执行截取OUTPUT_FILE="/mnt/g/youtube-rensheng/251111-6jtqrs/temp/1762832185804.mp4"ffmpeg -ss $START_TIME -t $ACTUAL_DURATION -i "$VIDEO_FILE" -c copy "$OUTPUT_FILE"看起来没什么问题,但就是执行失败。
问题一:文件路径不对(最明显的坑)
仔细看第一行:
VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/"发现问题了吗?这是一个目录路径,不是文件路径!
FFmpeg 需要的是具体的视频文件,比如:
VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/video.mp4"如果你只给它一个目录,FFmpeg 会一脸懵逼,不知道该处理哪个文件。
怎么修复?
最简单的方法就是确保路径指向具体的文件:
# 错误示例VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/"
# 正确示例VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/my_video.mp4"或者,如果你想处理目录下的某个文件,可以用通配符:
VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/*.mp4"问题二:参数顺序错了(隐蔽的坑)
即使文件路径对了,还有一个更隐蔽的问题。
原来的命令是这样的:
ffmpeg -ss $START_TIME -t $ACTUAL_DURATION -i "$VIDEO_FILE" -c copy "$OUTPUT_FILE"把 -ss 和 -t 参数放在 -i 之前,虽然能快速定位到指定位置,但有个副作用:时间戳可能不准确。
这会导致什么问题呢?
- 截取出来的视频可能时长不对
- 可能会出现空文件
- 视频开头可能不是你想要的位置
怎么修复?
把 -ss 参数移到 -i 之后:
# 错误示例ffmpeg -ss $START_TIME -t $ACTUAL_DURATION -i "$VIDEO_FILE" -c copy "$OUTPUT_FILE"
# 正确示例ffmpeg -i "$VIDEO_FILE" -ss $START_TIME -t $ACTUAL_DURATION -c copy "$OUTPUT_FILE"这样虽然会慢一点(因为要先读取整个视频),但时间戳会准确很多。
完整的解决方案
结合上面两个问题,这是修复后的完整脚本:
#!/bin/bash
# 确保指向具体的视频文件VIDEO_FILE="/mnt/d/Downloads/movie/a45video/XF1327/renwu/specific_video.mp4"OUTPUT_FILE="/mnt/g/youtube-rensheng/251111-6jtqrs/temp/1762832185804.mp4"
# 检查文件是否存在if [ ! -f "$VIDEO_FILE" ]; then echo "错误:视频文件不存在 - $VIDEO_FILE" exit 1fi
# 获取视频时长(添加错误处理)DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$VIDEO_FILE")
if [ -z "$DURATION" ] || [ "$DURATION" = "N/A" ]; then echo "错误:无法获取视频时长" exit 1fi
# 确保数值有效DURATION_FLOAT=$(printf "%.6f" "$DURATION")
# 这里是你原有的时间计算逻辑# ...
# 执行截取(改进的命令)ffmpeg -i "$VIDEO_FILE" -ss $START_TIME -t $ACTUAL_DURATION -c copy -avoid_negative_ts 1 "$OUTPUT_FILE"
# 检查执行结果if [ $? -eq 0 ]; then echo "视频截取成功: $OUTPUT_FILE"else echo "视频截取失败" exit 1fi关键改进点
-
文件路径验证
- 检查文件是否存在:
[ ! -f "$VIDEO_FILE" ] - 确保路径指向具体文件,不是目录
- 检查文件是否存在:
-
参数顺序调整
- 把
-ss移到-i之后 - 提高时间戳的准确性
- 把
-
添加
-avoid_negative_ts 1- 防止时间戳问题导致的截取错误
- 这个参数在处理某些特殊编码的视频时很有用
-
错误处理
- 检查文件是否存在
- 检查视频时长是否能获取
- 检查 FFmpeg 命令是否执行成功
如果视频本身有问题怎么办?
有些视频可能有编码问题,用 -c copy 直接复制流可能会失败。
这时候可以考虑重新编码:
ffmpeg -i "$VIDEO_FILE" -ss $START_TIME -t $ACTUAL_DURATION \ -c:v libx264 -c:a aac -avoid_negative_ts 1 "$OUTPUT_FILE"虽然会慢一些,但更稳定。
预防措施:避免踩同样的坑
为了避免以后再遇到类似问题,建议:
1. 在执行前先检查视频
# 用 ffprobe 检查视频信息ffprobe -v error -show_entries format=duration,streams=codec_name "$VIDEO_FILE"这会告诉你视频的时长、编码格式等信息,方便判断是否有问题。
2. 对问题视频先转码
如果视频本身有问题,先转码再处理:
# 先转码ffmpeg -i "$VIDEO_FILE" -c:v libx264 -c:a aac temp_video.mp4
# 再截取ffmpeg -i temp_video.mp4 -ss $START_TIME -t $ACTUAL_DURATION -c copy output.mp43. 添加详细的日志
在脚本里多加一些日志输出:
echo "视频文件: $VIDEO_FILE"echo "视频时长: $DURATION 秒"echo "开始时间: $START_TIME 秒"echo "截取时长: $ACTUAL_DURATION 秒"echo "输出文件: $OUTPUT_FILE"这样出问题时能快速定位。
常见问题排查
问题 1:截取出来的视频是空的
可能原因:
- 开始时间超过了视频时长
- 视频本身有问题
解决方法:
# 检查视频时长ffprobe -v error -show_entries format=duration -of csv=p=0 "$VIDEO_FILE"
# 确保开始时间在合理范围内问题 2:截取出来的视频时长不对
可能原因:
-ss参数位置不对- 时间戳计算错误
解决方法:
# 把 -ss 放在 -i 之后ffmpeg -i "$VIDEO_FILE" -ss $START_TIME -t $ACTUAL_DURATION ...问题 3:视频开头有花屏或黑屏
可能原因:
- 关键帧位置不对
解决方法:
# 添加 -accurate_seek 参数ffmpeg -ss $START_TIME -i "$VIDEO_FILE" -t $ACTUAL_DURATION -c copy -avoid_negative_ts 1 "$OUTPUT_FILE"最后的话
FFmpeg 视频截取看似简单,但有很多细节需要注意。
这次遇到的问题主要有两个:
- 文件路径错误:指向了目录而不是文件
- 参数顺序错误:
-ss放在了-i之前
额外提示:在 n8n 的 Execute Command 节点中,如果命令比较复杂,建议先在终端里测试通过,再放到 n8n 里执行。这样调试起来会方便很多。