如何解决使用io将字节传递给python中的ffmpeg
抱歉,刚接触 stackoverflow
只是想知道是否可以从io传递字节数据。
我正在尝试使用 ffmpeg 从 gif 中提取帧,然后使用 Pillow 调整其大小。
我知道您可以使用 Pillow 从 gif 中提取帧,但有时它会删除某些 gif。所以我使用 ffmpeg 作为修复。
至于为什么我希望从内存中读取 gif 是因为我要更改此设置,因此来自 url 的 gif 将包含在 Bytesio 中而不是保存。
至于为什么我有额外的 Pillow 代码,我确实通过将实际文件名传递给 ffmpeg 命令来成功使其工作。
original_pil = Image.open("1.gif")
bytes_io = open("1.gif","rb")
bytes_io.seek(0)
ffmpeg = 'ffmpeg'
cmd = [ffmpeg,'-i','-','-vsync','0','-f','image2pipe','-pix_fmt','rgba','-vcodec','png','-report','-']
depth = 4
width,height = original_pil.size
buf_size = depth * width * height + 100
nbytes = width * height * 4
proc = SP.Popen(cmd,stdout=SP.PIPE,stdin=SP.PIPE,stderr=SP.PIPE,bufsize=buf_size,shell=False)
out,err = proc.communicate(input=bytes_io.read(),timeout=None)
FFMPEG 报告:
ffmpeg started on 2021-06-07 at 18:58:14
Report written to "ffmpeg-20210607-185814.log"
Command line:
ffmpeg -i - -vsync 0 -f image2pipe -pix_fmt rgba -vcodec png -report -
ffmpeg version 4.2.4-1ubuntu0.1 copyright (c) 2000-2020 the FFmpeg developers
built with gcc 9 (Ubuntu 9.3.0-10ubuntu2)
configuration: --prefix=/usr --extra-version=1ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --ena WARNING: library configuration mismatch
avcodec configuration: --prefix=/usr --extra-version=1ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enab libavutil 56. 31.100 / 56. 31.100
libavcodec 58. 54.100 / 58. 54.100
libavformat 58. 29.100 / 58. 29.100
libavdevice 58. 8.100 / 58. 8.100
libavfilter 7. 57.100 / 7. 57.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 5.100 / 5. 5.100
libswresample 3. 5.100 / 3. 5.100
libpostproc 55. 5.100 / 55. 5.100
Splitting the commandline.
Reading option '-i' ... matched as input url with argument '-'.
Reading option '-vsync' ... matched as option 'vsync' (video sync method) with argument '0'.
Reading option '-f' ... matched as option 'f' (force format) with argument 'image2pipe'.
Reading option '-pix_fmt' ... matched as option 'pix_fmt' (set pixel format) with argument 'rgba'.
Reading option '-vcodec' ... matched as option 'vcodec' (force video codec ('copy' to copy stream)) with argument 'png'.
Reading option '-report' ... matched as option 'report' (generate a report) with argument '1'.
Reading option '-' ... matched as output url.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option vsync (video sync method) with argument 0.
Applying option report (generate a report) with argument 1.
Successfully parsed a group of options.
Parsing a group of options: input url -.
Successfully parsed a group of options.
opening an input file: -.
[NULL @ 0x55b59c38f7c0] opening 'pipe:' for reading
[pipe @ 0x55b59c390240] Setting default whitelist 'crypto'
[gif @ 0x55b59c38f7c0] Format gif probed with size=2048 and score=100
[AVIOContext @ 0x55b59c398680] Statistics: 4614093 bytes read,0 seeks
pipe:: Input/output error
解决方法
对于单个图像,您的代码运行良好。
看起来您最后缺少 proc.wait()
,仅此而已。
对于多张图片,您可以查看我的帖子 here。
您可以简化代码,用于处理图像。
我对您的代码进行了一些更改,以使其更优雅(我认为):
- 您不需要
'-vsync','0'
参数。 - 我将
'-'
替换为'pipe:'
(我认为更清楚)。 - 除非您知道默认值太小,否则您不需要设置
bufsize
。 - 我删除了
stderr=SP.PIPE
,因为我习惯于在控制台中看到 FFmpeg 日志。 - 我在
proc.wait()
之后添加了proc.communicate
。
代码示例首先构建用于测试的合成 GIF 图像文件。
这是代码示例:
import subprocess as sp
import shlex
from PIL import Image
from io import BytesIO
# Build synthetic image tmp.gif for testing
sp.run(shlex.split(f'ffmpeg -y -f lavfi -i testsrc=size=128x128:rate=1:duration=1 tmp.gif'))
original_pil = Image.open('tmp.gif')
bytes_io = open('tmp.gif',"rb")
bytes_io.seek(0)
ffmpeg = 'ffmpeg'
cmd = [ffmpeg,'-i','pipe:',#'-vsync','0','-f','image2pipe','-pix_fmt','rgba','-vcodec','png','-report','pipe:']
proc = sp.Popen(cmd,stdout=sp.PIPE,stdin=sp.PIPE)
out = proc.communicate(input=bytes_io.read())[0]
proc.wait()
bytes_io_png = BytesIO(out)
img = Image.open(bytes_io_png)
img.show()
传递多张图片:
如果有多张图片,只有当所有图片都在内存中时才可以使用proc.communicate
。
与其将所有图像抓取到 RAM 中,然后将图像传递给 FFmpeg,不如使用编写器线程和 for 循环。
我尝试传递 PNG 图片,但它太乱了。
我更改了代码以传递 RAW 格式的图像。
RAW 图像的优点是所有图像的字节大小都是预先已知的。
这是一个代码示例(不使用 BytesIO):
import numpy as np
import subprocess as sp
import shlex
from PIL import Image
import threading
# Write gif images to stdin pipe.
def writer(stdin_pipe):
# Write 30 images to stdin pipe (for example)
for i in range(1,31):
in_file_name = 'tmp' + str(i).zfill(2) + '.gif'
with open(in_file_name,"rb") as f:
proc.stdin.write(f.read()) # Write bytes to stdin pipe
stdin_pipe.close()
# Build 30 synthetic images tmp01.gif,tmp02.gif,...,tmp31.gif for testing
sp.run(shlex.split(f'ffmpeg -y -f lavfi -i testsrc=size=128x128:rate=1:duration=30 -f image2 tmp%02d.gif'))
original_pil = Image.open("tmp01.gif")
depth = 4
width,height = original_pil.size
nbytes = width * height * 4
ffmpeg = 'ffmpeg'
cmd = [ffmpeg,'rawvideo',# Select rawvideo codec
'-report','pipe:']
proc = sp.Popen(cmd,stdin=sp.PIPE)
thread = threading.Thread(target=writer,args=(proc.stdin,))
thread.start() # Strat writer thread
while True:
in_bytes = proc.stdout.read(nbytes) # Read raw image bytes from stdout pipe.
raw_imag = np.frombuffer(in_bytes,np.uint8).reshape([height,width,4])
img = Image.fromarray(raw_imag)
img.show()
# Break the loop when number of bytes read is less then expected size.
if len(in_bytes) < nbytes:
break
proc.wait()
thread.join()
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。