使用 OpenCV VideoWriter 和 Python BytesIO 在内存中流式传输视频

问题描述 投票:0回答:3

我想知道是否可以使用 Python 中的 OpenCV

VideoWriter
类“流式传输”数据?

通常,为了处理内存中本来会写入磁盘的数据,我使用 BytesIO(或 StringIO)。

我尝试使用 BytesIO 但失败了:

import cv2
from io import BytesIO

stream = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('x264')

data = BytesIO()

# added these to try to make data appear more like a string
data.name = 'stream.{}'.format('av1')
data.__str__ = lambda x: x.name

try:
    video = cv2.VideoWriter(data, fourcc=fourcc, fps=30., frameSize=(640, 480))
    start = data.tell()

        # Check if camera opened successfully
        if (stream.isOpened() == False): 
            print("Unable to read camera feed", file=sys.stderr)
            exit(1)

        # record loop
        while True:
            _, frame = stream.read()
            video.write(frame)
            data.seek(start)
            # do stuff with frame bytes
            # ...

            data.seek(start)

    finally:
        try:
            video.release()
        except:
            pass
finally:
    stream.release()

但是,我最终没有编写

BytesIO
对象,而是收到以下消息:

Traceback (most recent call last):
  File "video_server.py", line 54, in talk_to_client
    video = cv2.VideoWriter(data, fourcc=fourcc, fps=fps, frameSize=(width, height))
TypeError: Required argument 'apiPreference' (pos 2) not found

...因此,当我将 VideoWriter 调用修改为

cv2.VideoWriter(data, apiPreference=0, fourcc=fourcc, fps=30., frameSize=(640, 480))
(我读到 0 表示自动,但我也尝试过
cv2.CAP_FFMPEG
)时,我反而收到以下错误:

Traceback (most recent call last):
  File "video_server.py", line 54, in talk_to_client
    video = cv2.VideoWriter(data, apiPreference=0, fourcc=fourcc, fps=fps, frameSize=(width, height))
TypeError: bad argument type for built-in operation

所以我的问题是,是否可以使用内存中的

cv2.VideoWriter
类编写编码视频,如果可以的话,是如何完成的?

此时我还没有什么想法,所以非常欢迎任何帮助:-)

python video-streaming opencv in-memory
3个回答
6
投票

不幸的是,OpenCV 不支持对内存进行编码(或解码)。您必须写入(或读取)磁盘,VideoWriter(或 VideoCapture)才能工作。


6
投票

如果您想方便地加载内存中已存在的视频,请使用临时文件:https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile

import tempfile
import cv2

my_video_bytes = download_video_in_memory()

with tempfile.NamedTemporaryFile() as temp:
   temp.write(my_video_bytes)

   video_stream = cv2.VideoCapture(temp.name)

   # do your stuff.
不幸的是,这仍然会在磁盘上创建一个文件。但是,嘿,至少你不必自己管理。有一个

SpooledTemporaryFile() 实现将保留在内存中,但不幸的是,它不会创建 OpenCV 可以引用的文件系统名称。

编辑更新:

进一步探索 Linux 界面,看起来您可以通过使用

tmpfs

实用程序来充分利用临时文件并使其仅存在于内存中。

在 Ubuntu 机器上测试,未指定 
dir

的 NamedTemporaryFile() 对象将遵循 TMPDIR(它恰好指向我机器上的

/tmp

。现在,不确定其他操作系统,但我的机器没有
/tmp
 安装在主分区上。
但是,创建自己的“内存文件系统”非常简单,如下所示:
tmpfs

如果我们检查

sudo mount -t tmpfs -o size=1G tmpfs my_folder

 的输出,我会看到:

df my_folder -h

真他妈邪恶!!

现在如果我在 Python 中执行此操作:
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           1.0G     0  1.0G   0% my_folder

我刚刚在 Linux 中使用

with tempfile.NamedTemporaryFile(dir="my_folder") as temp:

在完全内存文件系统中创建了一个临时文件。当然,我确实会产生将数据从 python 内存复制到另一个内存位置的成本。但是,嘿,对于大文件,这可能比写入磁盘更便宜。
确保您的内存文件系统有足够的内存来保存视频文件,否则,根据设计,

mkstemp

无论如何都会利用交换空间将文件放在磁盘上。

编辑更新

Kubernetes 还允许您直接在 Pod 或 Deployment 上执行此操作。因此,您可以直接在容器的文件系统上创建一个 TMPFS 目录,然后就可以开始了!

tmpfs

编辑完整更新

使用
volumes:
- name: data
  emptyDir:
    medium: Memory
    sizeLimit: 64Mi
volumeMounts:
- mountPath: /tmp/data
  name: data

cv2.VideoWriter

类,并不重要,您可以传入任意 FFMPEG 或 Gstreamer 管道,具体取决于您是否使用 cv2.CAP_FFMPEG 或 cv2.CAP_GSTREAMER。例如,使用 Gstreamer(您通常必须构建支持 Gstreamer 的 OpenCV),您可以分别使用

cv2.VideoCapture

shmsrc
 在内存中流式传输视频。 
https://gstreamer.freedesktop.org/documentation/shm/shmsink.html?gi-language=c

整个教程系列必须涵盖 GStreamer,但只需搜索 Gstreamer+Opencv 示例,您就会明白。

如果您使用Linux,您可以创建一个ramdisk并对其进行写入。

shmsink

3
投票

© www.soinside.com 2019 - 2024. All rights reserved.