我有一个应用程序,它大约需要 25 秒来处理视频的每一帧,然后它将每个处理后的帧保存到一个专用目录中,该目录只包含帧,并且每个帧都由其在视频中的位置命名,没有填充。
然而,每个输出帧大约为 30MB,而如果要单独处理 1 小时 60fps 输入中的每一帧,然后最后将它们全部编码为 MP4,则需要大约 7TB 的空间。
问题是我只有大约 4TB 可用。所以我想要的是,当我的处理应用程序将帧写入磁盘时,FFMPEG 应该识别出有新的帧可供编码,对其进行编码,然后删除原始帧。这应该会大大减少所需的磁盘存储量(根据我当前的编码器设置),即使对于我可能处理的最长的视频输入也是如此。
基本上,我希望 FFMPEG 从一系列图像中编码出一个视频,但我希望它等待每个帧可用,然后尖叫错误代码并退出。我事先知道预期输出中的确切帧数,但我不知道如何告诉 FFMPEG。我已经有一个正在使用的命令,但它不会让 FFMPEG 等待新的输入帧变得可读,也不会删除已编码的帧。
ffmpeg -r 60 -f image2 -i processed/%d.png -vf "scale=3840:2160:force_original_aspect_ratio=1" -c:v hevc_nvenc -b:v 45M -r 60 -preset slow -pix_fmt yuv444p output.mp4
答案1
我通过调用 ImageMagick 将输出帧缩放为处理程序输出的 66%,从而解决了我自己的特定用例的这个问题。在这种情况下,ImageMagick 的输出像素大小仍然大于 4K 分辨率,而我的视频输出将是 4K 分辨率。这使得存储要求稍微可以承受一些,但是,除了 ~50:00 的视频之外,几乎没有回旋余地,因为我可用的存储空间“有限”。
这只是我对这个独特问题的独特解决方案,但鼓励每个人都添加适当的答案。
答案2
只需ffmpeg
从 FIFO 读取其输入,然后只要 FIFO 正在被写入,它就会挂起,直到生成每个帧为止。
ffmpeg
以下示例以 RGB888 格式生成 100 帧 640x480 视频,并在将每帧写入正在读取的FIFO 之间暂停几秒钟:
#!/bin/bash
SIZE="640x480"
# Make a FIFO to write to and for ffmpeg to read from
[ ! -p video ] && mkfifo video
# Genrate 100 frames of random, solid colour video of 640x480
for ((i=0;i<100;i++)) ; do
# Generate random RGB colour
((R=RANDOM%255))
((G=RANDOM%255))
((B=RANDOM%255))
>&2 echo "Sending frame $i with rgb($R,$G,$B):"
magick -size "$SIZE" xc:"rgb($R,$G,$B)" -depth 8 rgb:-
((t=RANDOM%10))
>&2 echo "Sleeping for $t seconds"
sleep $t
done > fifo &
# Start ffmpeg reading frames from FIFO
ffmpeg -f rawvideo -video_size $SIZE -pixel_format rgb24 -framerate 1 -i fifo -pix_fmt yuv420p video.mp4
请注意,我将调试信息写入 ,stderr
以便>&2 echo
不会无意中将其发送到ffmpeg
。请注意,您也可以使用 Python 或 C/C++ 生成帧,而不必使用图像魔术师。
如果更容易理解的话,复合的多行语句bash
相当于同样的事情:
{
generate frame on stdout using Python/ImageMagick or other
sleep 25
generate frame on stdout using Python/ImageMagick or other
sleep 25
generate frame on stdout using Python/ImageMagick or other
sleep 25
generate frame on stdout using Python/ImageMagick or other
sleep 25
} | ffmpeg -f rawvideo -video_size $SIZE -pixel_format rgb24 -framerate 1 /dev/stdin ... output.mp4