Fix V4L2 mmap capture and service configuration

- capture.py: use mmap.mmap instead of non-existent os.mmap
- capture.py: fix v4l2_fourcc() to pass 4 char arguments
- camera-webui.service: add Group=video and PATH env var
This commit is contained in:
mikkeli
2026-08-06 09:21:02 +09:00
parent d4eebf79f6
commit b1306bac21
2 changed files with 12 additions and 7 deletions
+2 -1
View File
@@ -6,8 +6,9 @@ Wants=network-online.target
[Service]
Type=exec
User=mikkeli
Group=video
WorkingDirectory=/home/mikkeli/dev/usb-camera-webui
ExecStartPre=/usr/bin/python3 -c "import v4l2"
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/usr/bin/python3 -m camera.app --host 0.0.0.0 --port 8080
Restart=on-failure
RestartSec=5
+10 -6
View File
@@ -1,16 +1,20 @@
"""V4L2 MJPEG capture via the v4l2 Python library (mmap).
"""V4L2 MJPEG capture via mmap buffers.
Uses memory-mapped buffers for efficient frame capture without subprocess
overhead. Each frame is a complete JPEG image ready for MJPEG streaming.
Opens /dev/video0 and uses memory-mapped V4L2 buffers for efficient frame
capture. Each frame is a complete JPEG image ready for MJPEG streaming.
"""
import fcntl
import os
import time
import threading
import mmap
import v4l2
# Fourcc for Motion JPEG
MJPG = v4l2.v4l2_fourcc("M", "J", "P", "G")
# Global camera singleton
_camera: "Capture | None" = None
_camera_lock = threading.Lock()
@@ -79,10 +83,10 @@ class Capture:
fmt.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
fmt.fmt.pix.width = self._width
fmt.fmt.pix.height = self._height
fmt.fmt.pix.pixelformat = v4l2.v4l2_fourcc("MJPG")
fmt.fmt.pix.pixelformat = MJPG
fmt.fmt.pix.field = v4l2.V4L2_FIELD_ANY
_ioctl(self._fd, v4l2.VIDIOC_S_FMT, fmt)
print(f"Format set: {fmt.fmt.pix.width}x{fmt.fmt.pix.height} {v4l2.v4l2_fourcc2str(fmt.fmt.pix.pixelformat)}")
print(f"Format: {fmt.fmt.pix.width}x{fmt.fmt.pix.height} {v4l2.v4l2_fourcc2str(fmt.fmt.pix.pixelformat)}")
def _allocate_buffers(self) -> None:
req = v4l2.v4l2_requestbuffers()
@@ -101,7 +105,7 @@ class Capture:
length = buf.length
offset = buf.m.offset
data = os.mmap(0, length, os.PROT_READ, os.MAP_SHARED, self._fd, offset)
data = mmap.mmap(self._fd, length, mmap.MAP_SHARED, mmap.PROT_READ, 0, offset)
self._buffers.append({
"index": i,
"length": length,