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] [Service]
Type=exec Type=exec
User=mikkeli User=mikkeli
Group=video
WorkingDirectory=/home/mikkeli/dev/usb-camera-webui 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 ExecStart=/usr/bin/python3 -m camera.app --host 0.0.0.0 --port 8080
Restart=on-failure Restart=on-failure
RestartSec=5 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 Opens /dev/video0 and uses memory-mapped V4L2 buffers for efficient frame
overhead. Each frame is a complete JPEG image ready for MJPEG streaming. capture. Each frame is a complete JPEG image ready for MJPEG streaming.
""" """
import fcntl import fcntl
import os import os
import time import time
import threading import threading
import mmap
import v4l2 import v4l2
# Fourcc for Motion JPEG
MJPG = v4l2.v4l2_fourcc("M", "J", "P", "G")
# Global camera singleton # Global camera singleton
_camera: "Capture | None" = None _camera: "Capture | None" = None
_camera_lock = threading.Lock() _camera_lock = threading.Lock()
@@ -79,10 +83,10 @@ class Capture:
fmt.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE fmt.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
fmt.fmt.pix.width = self._width fmt.fmt.pix.width = self._width
fmt.fmt.pix.height = self._height 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 fmt.fmt.pix.field = v4l2.V4L2_FIELD_ANY
_ioctl(self._fd, v4l2.VIDIOC_S_FMT, fmt) _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: def _allocate_buffers(self) -> None:
req = v4l2.v4l2_requestbuffers() req = v4l2.v4l2_requestbuffers()
@@ -101,7 +105,7 @@ class Capture:
length = buf.length length = buf.length
offset = buf.m.offset 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({ self._buffers.append({
"index": i, "index": i,
"length": length, "length": length,