Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace with linuxpy #48

Merged
merged 4 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

strategy:
matrix:
python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
python-version: [3.9, "3.10", "3.11", "3.12"]

steps:
- name: Checkout
Expand All @@ -31,7 +31,7 @@ jobs:
- name: Linting tests
run: |
black --check --diff v4l2py tests examples
ruff check --diff --format=github --show-files .
ruff check --diff --output-format=github --show-files .
- name: Build package
run: |
python -m build
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# v4l2py

**Since version 3.0 this project is now a shim for `linuxpy.video`**

**Please consider using [linuxpy](https/github.com/tiagocoutinho/linuxpy) directly.**

[![V4L2py][pypi-version]](https://pypi.python.org/pypi/v4l2py)
[![Python Versions][pypi-python-versions]](https://pypi.python.org/pypi/v4l2py)
![License][license]
Expand Down
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@ license = {text = "GPL-3.0-or-later"}
authors = [
{ name = "Jose Tiago Macara Coutinho", email = "[email protected]" }
]
requires-python = ">=3.7"
requires-python = ">=3.9"
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Multimedia :: Video",
"Topic :: Multimedia :: Video :: Capture",
]
dependencies = []
dependencies = ["linuxpy>=0.9.0"]
dynamic = ["version", "readme"]

[project.urls]
Expand Down
57 changes: 33 additions & 24 deletions tests/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# Copyright (c) 2021 Tiago Coutinho
# Distributed under the GPLv3 license. See LICENSE for more info.

from contextlib import ExitStack
import os
from contextlib import ExitStack, contextmanager
from errno import EINVAL
from inspect import isgenerator
from math import isclose
Expand Down Expand Up @@ -62,11 +63,11 @@ def __init__(self, filename="/dev/video39"):

def __enter__(self):
self.stack = ExitStack()
ioctl = mock.patch("v4l2py.device.fcntl.ioctl", self.ioctl)
opener = mock.patch("v4l2py.io.open", self.open)
mmap = mock.patch("v4l2py.device.mmap.mmap", self.mmap)
select = mock.patch("v4l2py.io.IO.select", self.select)
blocking = mock.patch("v4l2py.device.os.get_blocking", self.get_blocking)
ioctl = mock.patch("linuxpy.ioctl.fcntl.ioctl", self.ioctl)
opener = mock.patch("linuxpy.io.open", self.open)
mmap = mock.patch("linuxpy.video.device.mmap.mmap", self.mmap)
select = mock.patch("linuxpy.io.IO.select", self.select)
blocking = mock.patch("linuxpy.device.os.get_blocking", self.get_blocking)
self.stack.enter_context(ioctl)
self.stack.enter_context(opener)
self.stack.enter_context(mmap)
Expand Down Expand Up @@ -99,45 +100,46 @@ def ioctl(self, fd, ioc, arg): # noqa: C901
if arg.index > 0:
raise OSError(EINVAL, "ups!")
arg.name = self.input0_name
arg.type = raw.V4L2_INPUT_TYPE_CAMERA
arg.type = raw.InputType.CAMERA
elif isinstance(arg, raw.v4l2_query_ext_ctrl):
if arg.index > 0:
raise OSError(EINVAL, "ups!")
arg.name = b"brightness"
arg.type = raw.V4L2_CTRL_TYPE_INTEGER
arg.type = raw.CtrlType.INTEGER
arg.id = 9963776
elif isinstance(arg, raw.v4l2_capability):
arg.driver = self.driver
arg.card = self.card
arg.bus_info = self.bus_info
arg.version = self.version
arg.capabilities = raw.Capability.STREAMING | raw.Capability.VIDEO_CAPTURE
elif isinstance(arg, raw.v4l2_format):
if ioc == raw.VIDIOC_G_FMT:
if ioc == raw.IOC.G_FMT:
arg.fmt.pix.width = 640
arg.fmt.pix.height = 480
arg.fmt.pix.pixelformat = raw.V4L2_PIX_FMT_RGB24
arg.fmt.pix.pixelformat = raw.PixelFormat.RGB24
elif isinstance(arg, raw.v4l2_buffer):
if ioc == raw.VIDIOC_QUERYBUF:
if ioc == raw.IOC.QUERYBUF:
pass
elif ioc == raw.VIDIOC_DQBUF:
elif ioc == raw.IOC.DQBUF:
arg.index = 0
arg.bytesused = len(self.frame)
arg.sequence = 123
arg.timestamp.secs = 123
arg.timestamp.usecs = 456789
elif ioc == raw.VIDIOC_STREAMON:
assert arg.value == raw.V4L2_BUF_TYPE_VIDEO_CAPTURE
elif ioc == raw.IOC.STREAMON:
assert arg.value == raw.BufType.VIDEO_CAPTURE
self.video_capture_state = "ON"
elif ioc == raw.VIDIOC_STREAMOFF:
assert arg.value == raw.V4L2_BUF_TYPE_VIDEO_CAPTURE
elif ioc == raw.IOC.STREAMOFF:
assert arg.value == raw.BufType.VIDEO_CAPTURE
self.video_capture_state = "OFF"
return 0

def mmap(self, fd, length, offset):
assert self.fd == fd
return MemoryMap(self)

def select(self, readers, writers, other):
def select(self, readers, writers, other, timeout=None):
assert readers[0].fileno() == self.fd
return readers, writers, other

Expand Down Expand Up @@ -165,6 +167,18 @@ def assert_frame(frame, camera):
assert numpy.all(frame.array == numpy.frombuffer(camera.frame, dtype="u1"))


@contextmanager
def video_files(paths=("/dev/video99")):
with mock.patch("linuxpy.device.pathlib.Path.glob") as glob:
expected_files = list(paths)
glob.return_value = expected_files
with mock.patch("linuxpy.device.pathlib.Path.is_char_device") as is_char_device:
is_char_device.return_value = True
with mock.patch("linuxpy.device.os.access") as access:
access.return_value = os.R_OK | os.W_OK
yield paths


@test("device number")
def _(
filename=each("/dev/video0", "/dev/video1", "/dev/video999"),
Expand All @@ -175,20 +189,15 @@ def _(

@test("video files")
def _():
with mock.patch("v4l2py.device.pathlib.Path.glob") as glob:
expected_files = ["/dev/video0", "/dev/video55"]
glob.return_value = expected_files

with video_files(["/dev/video0", "/dev/video55"]) as expected_files:
assert list(iter_video_files()) == expected_files


@test("device list")
def _():
assert isgenerator(iter_devices())

with mock.patch("v4l2py.device.pathlib.Path.glob") as glob:
expected_files = ["/dev/video0", "/dev/video55"]
glob.return_value = expected_files
with video_files(["/dev/video0", "/dev/video55"]) as expected_files:
devices = list(iter_devices())
assert len(devices) == 2
for device in devices:
Expand Down
8 changes: 7 additions & 1 deletion v4l2py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

# ruff: noqa: F401

import warnings

from .device import (
Device,
Frame,
Expand All @@ -19,4 +21,8 @@
)
from .io import IO, GeventIO

__version__ = "2.3.0"
__version__ = "3.0.0"

warnings.warn(
"v4l2py is no longer being maintained. Please consider using linuxpy.video instead"
)
Loading
Loading