如何让FFmpeg将子秒级间隔的分段视频立即写入磁盘(树莓派录制场景)
如何让FFmpeg将子秒级间隔的分段视频立即写入磁盘(树莓派录制场景)
我之前在树莓派上做视频录制备份的时候,刚好碰到过一模一样的问题——断电后分段文件全是空的,用了-strftime又遇到子秒级命名重复覆盖的坑。给你两个亲测可行的解决方案:
方案一:不用-strftime,强制FFmpeg实时刷新分段文件到磁盘
你遇到的空文件问题,本质是MP4容器的特性:它默认会把关键元数据(比如moov atom)缓存到内存,直到文件正常关闭才写入磁盘,断电时这些缓存内容直接丢失,导致文件变成空的。我们不需要依赖-strftime来触发写入,直接给MP4分段器加参数就能强制实时刷新:
修改你的FFmpeg命令,重点调整-segment_format_options部分:
libcamera-vid --flush \ --framerate ${FRAMERATE} \ --width ${WIDTH} \ --height ${HEIGHT} \ -n \ -t ${TIMEOUT} \ --codec yuv420 \ -o - | ffmpeg \ -fflags nobuffer \ -strict experimental \ -loglevel debug \ -flags low_delay \ -f rawvideo \ -pix_fmt yuv420p \ -s:v ${WIDTH}x${HEIGHT} \ -r ${FRAMERATE} \ -i - \ -c:v h264_v4l2m2m \ -f segment \ -segment_time 0.1 \ -segment_format mp4 \ # 新增的关键参数,强制实时刷新磁盘 -segment_format_options movflags=flush_packets+empty_moov+faststart \ -reset_timestamps 1 \ -b:v ${ENCODING_BITRATE} \ -g 1 \ "output_%04d.mp4"
参数解释:
movflags=flush_packets:强制FFmpeg把每个视频数据包立即刷新到磁盘,不再缓存empty_moov:在文件开头生成一个空的moov元数据块,后续实时更新内容,避免依赖最终写入faststart:把moov元数据移到文件头部,方便后续直接播放分段文件(可选但推荐)
另外你已经给libcamera-vid加了--flush,这个很关键,能确保摄像头的原始数据不被缓存,直接传给FFmpeg,避免上游环节的延迟。
方案二:修复-strftime的子秒级命名问题,保留实时写入特性
如果你还是想用-strftime来触发实时写入(虽然方案一更直接),可以用FFmpeg扩展的strftime格式来生成唯一的子秒级文件名:
把输出文件名从"output_%04d.mp4"改成:
"output_%s%3N.mp4"
这样每个100ms的分段会生成类似output_1700000000123.mp4的文件名:
%s是当前Unix时间戳(秒级)%3N是毫秒部分(FFmpeg对标准strftime的扩展,%6N是微秒,%9N是纳秒)
这个格式能保证每个分段的文件名唯一,不会因为子秒级分段而被覆盖。如果你想同时保留序列数,也可以写成"output_%s%3N_%04d.mp4",兼顾时间戳和序号标识。
注意:你之前试的
%f是Python等语言的格式,FFmpeg的strftime不支持,所以才会直接输出%f字符串。
备注:内容来源于stack exchange,提问作者amfast




