python-3.x - 使用 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()
最后不见了,就是这样。
对于多张图片,您可以在此处查看我的帖子。
您可以简化代码以处理图像。
我对您的代码进行了一些更改,以使其更加优雅(我认为):
- 你不需要
'-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 中时才能使用。
与其将所有图像抓取到 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,
'-i', 'pipe:',
'-f', 'image2pipe',
'-pix_fmt', 'rgba',
'-vcodec', 'rawvideo', # Select rawvideo codec
'-report',
'pipe:']
proc = sp.Popen(cmd, stdout=sp.PIPE, 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()
推荐阅读
- python - 是否可以在 odoo 10 社区版中创建应用程序和模块
- android - 迁移到 AndroidX 导致一切崩溃
- ubuntu - 无法将模块插入内核
- python - 稀疏矩阵是否适用于 MultinomialNB?
- html - 如何使用 HTML 和 CSS 实现此上下文?
- python - 通过 Gmail API 发送后损坏的 pdf
- mysql - 如果选择子查询返回null,则忽略插入 - Mysql
- python - 将 sklearn RandomForestRegressor 绘制为一棵树
- powershell - 如何使用 Powershell 生成一封电子邮件,其中包含具有办公室特定配色方案的未知表格大小?
- php - Codeigniter 迁移 mysql 到 mysqli 连接错误