v4l2 Python - 流视频 - 映射缓冲区

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

我正在使用Raspbian(Raspberry Pi 2)中的Python视频捕获脚本,我在使用Python绑定v4l2时遇到了麻烦,因为我没有成功地对缓冲区进行内存管理。

我需要的:

  • 从HD-WebCam捕获视频(稍后将同时播放2个)。
  • 能够通过WLAN传输该视频(在网络负载和处理速度之间进行折衷)。
  • 将来,能够在流式传输之前对图像应用过滤器(非强制性)。

我尝试过的:

  • 使用OpenCV(cv2)。它非常易于使用,但它增加了大量处理负荷,因为它将网络摄像头的JPEG帧转换为原始图像,然后我必须将它们转换回JPEG,然后再通过WLAN发送。
  • 直接从'/ dev / video0'阅读。这将是伟大的,因为网络摄像头发送已经压缩的帧,我可以只是阅读和发送它们,但似乎我的相机不支持。
  • 对Python使用v4l2绑定。这是目前最有前途的选择,但是当我不得不映射视频缓冲区时,我陷入困境。我发现无法克服这些东西似乎需要的“内存指针/映射”。

我读过的内容:

我的问题:

  1. 有一个更好的方法吗?如果不是......
  2. 我可以告诉OpenCV不解压缩图像吗?最好使用OpenCV来应用未来的扩展 。我发现here不允许这样做。
  3. 我怎样才能解决Python中的映射步骤? (任何工作的例子?)

这是我在OpenCV上的(慢慢)工作示例:

import cv2
import time

video = cv2.VideoCapture(0)

print 'Starting video-capture test...'

t0 = time.time()
for i in xrange(100):
    success, image = video.read()
    ret, jpeg = cv2.imencode('.jpg',image)

t1 = time.time()
t = ( t1 - t0 ) / 100.0
fps = 1.0 / t

print 'Test finished. ' + str(t) + ' sec. per img.'
print str( fps ) + ' fps reached'

video.release()

在这里我用v4l2做了什么:

FRAME_COUNT = 5

import v4l2
import fcntl
import mmap

def xioctl( fd, request, arg):

    r = 0

    cond = True
    while cond == True:
        r = fcntl.ioctl(fd, request, arg)
        cond = r == -1
        #cond = cond and errno == 4

    return r

class buffer_struct:
    start  = 0
    length = 0

# Open camera driver
fd = open('/dev/video1','r+b')

BUFTYPE = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
MEMTYPE = v4l2.V4L2_MEMORY_MMAP

# Set format
fmt = v4l2.v4l2_format()
fmt.type = BUFTYPE
fmt.fmt.pix.width       = 640
fmt.fmt.pix.height      = 480
fmt.fmt.pix.pixelformat = v4l2.V4L2_PIX_FMT_MJPEG
fmt.fmt.pix.field       = v4l2.V4L2_FIELD_NONE # progressive

xioctl(fd, v4l2.VIDIOC_S_FMT, fmt)

buffer_size = fmt.fmt.pix.sizeimage
print "buffer_size = " + str(buffer_size)

# Request buffers
req = v4l2.v4l2_requestbuffers()

req.count  = 4
req.type   = BUFTYPE
req.memory = MEMTYPE

xioctl(fd, v4l2.VIDIOC_REQBUFS, req)

if req.count < 2:
    print "req.count < 2"
    quit()

n_buffers = req.count

buffers = list()
for i in range(req.count):
    buffers.append( buffer_struct() )

# Initialize buffers. What should I do here? This doesn't work at all.
# I've tried with USRPTR (pointers) but I know no way for that in Python.
for i in range(n_buffers):

    buf = v4l2.v4l2_buffer()

    buf.type      = BUFTYPE
    buf.memory    = MEMTYPE
    buf.index     = i

    xioctl(fd, v4l2.VIDIOC_QUERYBUF, buf)

    buffers[i].length = buf.length
    buffers[i].start  = mmap.mmap(fd.fileno(), buf.length,
                                  flags  = mmap.PROT_READ,# | mmap.PROT_WRITE,
                                  prot   = mmap.MAP_SHARED,
                                  offset = buf.m.offset )

我将不胜感激任何帮助或建议。非常感谢!

python-2.7 opencv video-capture v4l2
3个回答
1
投票

只是为了在我刚刚发现的地方添加另一个选项,您也可以将V4L2后端与OpenCV一起使用。

您只需在VideoCapture构造函数中指定它。例如

cap = cv2.VideoCapture()

cap.open(0, apiPreference=cv2.CAP_V4L2)

cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960)
cap.set(cv2.CAP_PROP_FPS, 30.0)

当没有明确指定时,OpenCV将经常使用另一个相机API(例如,gstreamer),这通常更慢且更麻烦。在这个例子中,我从限制在4-5 FPS到最高15到720p(使用Intel Atom Z8350)。

如果您希望将它与环形缓冲区(或其他内存映射缓冲区)一起使用,请查看以下资源:

https://github.com/Battleroid/seccam

https://github.com/bslatkin/ringbuffer


0
投票

为什么你不能使用Raspberry Distribution附带的python picamera lib

import io
    import socket
    import struct
    import time
    import picamera


    # create socket and bind host
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(('192.168.1.101', 8000))
    connection = client_socket.makefile('wb')

    try:
        with picamera.PiCamera() as camera:
            camera.resolution = (320, 240)      # pi camera resolution
            camera.framerate = 15               # 15 frames/sec
            time.sleep(2)                       # give 2 secs for camera to initilize
            start = time.time()
            stream = io.BytesIO()

            # send jpeg format video stream
            for foo in camera.capture_continuous(stream, 'jpeg', use_video_port = True):
                connection.write(struct.pack('<L', stream.tell()))
                connection.flush()
                stream.seek(0)
                connection.write(stream.read())
                if time.time() - start > 600:
                    break
                stream.seek(0)
                stream.truncate()
        connection.write(struct.pack('<L', 0))
    finally:
        connection.close()
        client_socket.close()

-1
投票

我自己找到了答案作为another question代码的一部分。这不是问题的主题,但在这个source code中你可以看到他如何在Python中使用mmap(第159行)。此外,我发现我不需要写权限。

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