Python并没有关于视频、音频的原生库,因此读取也好、写入也好都要借助第三方库。 业内最好的第三方库可能就是FFmpeg了。要在Python中使用ffmpeg, 可以使用PyFFmpeg等绑定库。但是对于简单的读写需求来说,这样的绑定库显得太过臃肿, 因此我们可以使用FFmpeg的command line来执行我们简单的读写需求,用Pipe控制FFmpeg的输入输出即可。 本人写的一个拙作在此 Gitee 仓库—>https://gitee.com/huzheyang/pyffpipe
用FFmpeg切割音频文件,请移步另一篇文章 对应的Github 仓库—>https://github.com/jsjyhzy/cuecut
读取帧
命令行:
ffmpeg.exe -i filepath -f image2pipe -pix_fmt rgb24 -vcodec rawvideo -
其中-i
指示了输入的视频文件,-f
指示了输出格式(要用pipe的话,这一项不能改),
-pix_fmt
指示了输出的色彩制式(这里使用rgb24,方便Pillow模块),-vcodec
指示了视频编码格式
(为了得到可直接导入Pillow的数据,使用了rawvideo),-
指示了pipe(不可忽略)
接下来只需要用过subprocess模块调用这个命令行,将输入流定义为pipe即可:
import subprocess as sp
pipe = sp.Popen(command, stdout=sp.PIPE, stderr=sp.STDOUT, bufsize=bytes_per_frame)
img_bytes = pipe.stdout.read(bytes_per_frame)
有必要的话可以将read过程包装成generator方便使用。
写入帧
命令行:
ffmpeg.exe -y -f rawvideo -vcodec rawvideo -s size -pix_fmt rgb24 \ -r frame_rate -i - -an -vcodec mpeg4 filepath
其中-y
指示若存在文件则覆盖(可选),-f
、-vcodec
、-pix_fmt
同上,-s
指示每一帧的尺寸大小(格式为:宽x高),
-r
指示帧率,-i -
指示从pipe输入,-an
指示没有音轨。
Python代码:
pipe = sp.Popen(command, stdin=sp.PIPE, stderr=sp.STDOUT, bufsize=bytes_per_frame)
pipe.stdin.write(img_bytes)
注意点
观察到ffmpeg在写入帧时默认会输出信息条,例如帧率,速度等信息。
输出的信息是以\r
结尾,达到覆盖输出的效果,但如果要把stderr
也用pipe连接,
则会导致read读不到\n
或EOF
,发生阻塞。虽然可以通过设置subprocess中universal_newlines=True
,
但这会导致该线程所有pipe都是以文本方式打开。因此要么不要用pipe连接stderr
,要么设置ffmpeg不回显信息。