当前位置: 首页 > 知识库问答 >
问题:

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

谷飞星
2023-03-14

我正在为Raspbian(Raspberry Pi 2)中的Python编写视频捕获脚本,我在使用v4l2的Python绑定时遇到了麻烦,因为我在内存映射缓冲区方面没有成功。

我所需要的:

  • 从高清网络摄像头捕获视频(稍后将同时捕获其中两个)

我所尝试的:

  • 使用OpenCV(cv2)。它很容易使用,但是它增加了很多处理负载,因为它将网络摄像头的JPEG帧转换为原始图像,然后我必须在通过WLAN发送它们之前将它们转换回JPEG。
  • 直接从/dev/video o0读取。这将是伟大的,因为网络摄像头发送的帧已经压缩,我可以只是阅读和发送它们,但似乎我的相机不支持。
  • 对Python使用v4l2绑定。这是目前最有希望的选择,但当我不得不映射视频缓冲区时,我陷入了困境。我没有找到克服内存指针/映射的方法,这东西似乎需要。

我读到的:

  • 本指南:http://www.jayrambhia.com/blog/capture-v4l2/
  • v4l2文档(其中一些)
  • C语言中的此示例:https://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
  • C/C中的一些其他示例。我没有发现在Python上直接使用v4l2绑定的示例

我的问题是:

  1. 有更好的方法吗?或者如果不是
  2. 我可以告诉OpenCV不要解压缩图像吗?为了应用未来的扩展,最好使用OpenCV 。我发现这是不允许的
  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 )

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

共有3个答案

邵城
2023-03-14

为什么不能使用Raspberry发行版附带的python picamera库

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()
蒋嘉实
2023-03-14

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

邢高澹
2023-03-14

为了在这里添加我刚刚发现的另一个选项,您还可以将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到720p时的15 FPS(使用Intel Atom Z8350)。

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

https://github.com/Battleroid/seccam

https://github.com/bslatkin/ringbuffer

 类似资料:
  • 问题内容: 如何在HTML5视频上强制中止事件?我有一个叠加层,当我关闭它时,视频应该暂停播放,然后 停止缓冲 。但是,我的互联网连接仍然发疯。哦,我在Mac OS X 10.6上使用Chrome 7.0.5。 我已经尝试了几件事-没有一个起作用: (对于那些不熟悉XUI的人,x $就像jQuery的包装函数一样) 首先,调度中止HTML事件: 接下来,更改src,然后强制加载: 编辑:我的视频元

  • 问题内容: 当我从互联网上加载视频(10-40MB大)时,无法提供流畅的播放体验。 我的AVPlayer要么加载整个视频然后播放,要么播放1s,缓冲然后停止播放。 我尝试了无尽的库,缓冲区观察者方法和教程。似乎没有任何帮助。 问题答案: 从 iOS 10.x开始 ,您可以进行一些缓冲设置,例如,您可以决定缓冲视频需要多少秒:

  • 我有一个blob数组(实际上是二进制数据--我可以表达它是最有效的。我现在使用Blobs,但可能或其他更好的方法)。每个Blob包含1秒的音频/视频数据。每秒都会生成一个新的Blob并将其追加到我的数组中。因此代码大致如下所示: 我的目标是将此音频/视频数据流式传输到HTML5元素。我知道Blob URL可以像下面这样生成和播放:

  • 如何更改仍为3M的缓冲区 当前启动命令: ffmpeg-f dshow-i video=“屏幕捕获录像机”-vcodec libx264-预设:v ultrafast-过滤器:v“crop=480:270:0:0”-vf tpad=start_duration=30-r 30-g 60-keyint_min 60-sc_阈值0-b:v 1G-最大速率2500k-bufsize 1G-rtbufsi

  • 现在我们先花点时间复习一下我们已经谈论过的三个东西:映射(mappings),缩写(abbreviations)和选项设置(options),这个过程中会讲到一些新的东西。我们将在一个单一的缓冲区中同时设置它们。 这一章所讲到的东西会在下一章中真正的显示它们的作用,目前我们只需先打下基础。 在这一章中你需要在Vim中打开两个文件,两个文件是分开的。我先将它们命名为foo和bar,你可以随便对它们命

  • 我现在正在Android上对一个h264字节流进行解码。流是从第三方产品发送的,我不太确定它的视频格式。该文件说,流由PPS和SPS NAL单位组成。但我接收到的h264字节流包括以0x00、0x00、0x00、0x01开头的序列,并且在我接收到的样本中,第5个字节可能是0x09、0x21或0x06。这让我兴奋了一段时间,因为它似乎与通常的0x67或0x68指示器不同。有人知道NAL单元头中的0x