From da9d8f95f9a0a8c5e0ddd1ecd4830717af2676b1 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Wed, 31 Jul 2024 10:26:08 +0200 Subject: [PATCH 1/5] Lazy controls --- linuxpy/video/device.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/linuxpy/video/device.py b/linuxpy/video/device.py index e66537e..ab8386a 100644 --- a/linuxpy/video/device.py +++ b/linuxpy/video/device.py @@ -1000,8 +1000,25 @@ def create_artificial_control_class(class_id): class Controls(dict): - @classmethod - def from_device(cls, device): + def __init__(self, device: Device): + super().__init__() + self.__dict__["_device"] = device + self.__dict__["_initialized"] = False + + def _init_if_needed(self): + if not self._initialized: + self._load() + self.__dict__["_initialized"] = True + + def __getitem__(self, name): + self._init_if_needed() + return super().__getitem__(name) + + def __len__(self): + self._init_if_needed() + return super().__len__() + + def _load(self): ctrl_type_map = { ControlType.BOOLEAN: BooleanControl, ControlType.INTEGER: IntegerControl, @@ -1013,9 +1030,8 @@ def from_device(cls, device): ControlType.U32: U32Control, ControlType.BUTTON: ButtonControl, } - ctrl_dict = {} classes = {} - for ctrl in device.info.controls: + for ctrl in self._device.info.controls: ctrl_type = ControlType(ctrl.type) ctrl_class_id = V4L2_CTRL_ID2CLASS(ctrl.id) if ctrl_type == ControlType.CTRL_CLASS: @@ -1030,9 +1046,12 @@ def from_device(cls, device): ctrl_class = CompoundControl else: ctrl_class = ctrl_type_map.get(ctrl_type, GenericControl) - ctrl_dict[ctrl.id] = ctrl_class(device, ctrl, klass) + self[ctrl.id] = ctrl_class(self._device, ctrl, klass) - return cls(ctrl_dict) + @classmethod + def from_device(cls, device): + """Deprecated: backward compatible. Please use Controls(device) constructor directly""" + return cls(device) def __getattr__(self, key): with contextlib.suppress(KeyError): From 7b279d0428291c710c368916fa327554804ea5f8 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 2 Aug 2024 08:02:06 +0200 Subject: [PATCH 2/5] Fix video control with_class --- linuxpy/video/device.py | 8 +++++++- tests/test_video.py | 42 ++++++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/linuxpy/video/device.py b/linuxpy/video/device.py index ab8386a..4dde4ed 100644 --- a/linuxpy/video/device.py +++ b/linuxpy/video/device.py @@ -1078,10 +1078,16 @@ def used_classes(self): return list(class_map.values()) def with_class(self, control_class): + if isinstance(control_class, str): + control_class = ControlClass[control_class.upper()] + elif isinstance(control_class, int): + control_class = ControlClass(control_class) + elif not isinstance(control_class, ControlClass): + control_class = ControlClass(control_class.id - 1) for v in self.values(): if not isinstance(v, BaseControl): continue - if v.control_class.id == control_class.id: + if v.control_class.id - 1 == control_class: yield v def set_to_default(self): diff --git a/tests/test_video.py b/tests/test_video.py index 781884d..1d046c8 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -626,12 +626,6 @@ def _(sink=output_type, source=input_type): assert frame.memory == Memory.MMAP if source is None else source assert frame.pixel_format == pixel_format - -for output_type in (None, Capability.STREAMING, Capability.READWRITE): - oname = "auto" if output_type is None else output_type.name - for input_type in (None, Capability.STREAMING, Capability.READWRITE): - iname = "auto" if input_type is None else input_type.name - @skip(when=not is_v4l2looback_prepared(), reason="v4l2loopback is not prepared") @skip(when=not is_v4l2loopback_installed(), reason="v4l2loopback is not installed") @test(f"output({oname}) and async capture({iname}) with v4l2loopback") @@ -682,7 +676,7 @@ def _(): current_value = controls.keep_format.value assert current_value in {True, False} try: - controls.keep_format.value = not current_value + controls.keep_format.value = current_value + 5 assert controls.keep_format.value == (not current_value) finally: controls.keep_format.value = current_value @@ -690,13 +684,43 @@ def _(): with raises(AttributeError): _ = controls.unknown_field - assert ControlClass.USER in controls.used_classes() + assert ControlClass.USER in {ctrl.id - 1 for ctrl in controls.used_classes()} assert controls.keep_format in controls.with_class("user") assert controls.keep_format in controls.with_class(ControlClass.USER) assert " Date: Fri, 2 Aug 2024 08:05:22 +0200 Subject: [PATCH 3/5] Adjust vivid in github actions --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4672a3..7c02289 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: cat /etc/udev/rules.d/99-$USER.rules sudo udevadm control --reload-rules sudo udevadm trigger - sudo modprobe vivid n_devs=2 node_types=0xe1d3d,0xe1d3d vid_cap_nr=10,20 vid_out_nr=11,21 meta_cap_nr=12,22 meta_out_nr=13,33 + sudo modprobe vivid n_devs=1 node_types=0xe1d3d vid_cap_nr=190 vid_out_nr=191 meta_cap_nr=192 meta_out_nr=193 lsmod - name: Set up Python ${{ matrix.python-version }} id: setuppy From ee43a14a18f935ce00cdcfc7dd86438914ea0bde Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 2 Aug 2024 08:07:30 +0200 Subject: [PATCH 4/5] Debug vivid perms in github actions --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c02289..0fa9d08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,11 +29,13 @@ jobs: sudo modprobe -i uinput echo KERNEL==\"uinput\", SUBSYSTEM==\"misc\" GROUP=\"docker\", MODE:=\"0666\" | sudo tee /etc/udev/rules.d/99-$USER.rules echo KERNEL==\"event[0-9]*\", SUBSYSTEM==\"input\" GROUP=\"docker\", MODE:=\"0666\" | sudo tee -a /etc/udev/rules.d/99-$USER.rules + echo SUBSYSTEM==\"video4linux\" GROUP=\"docker\", MODE:=\"0666\" | sudo tee -a /etc/udev/rules.d/99-$USER.rules cat /etc/udev/rules.d/99-$USER.rules sudo udevadm control --reload-rules sudo udevadm trigger sudo modprobe vivid n_devs=1 node_types=0xe1d3d vid_cap_nr=190 vid_out_nr=191 meta_cap_nr=192 meta_out_nr=193 lsmod + ls -lsa /dev/video* - name: Set up Python ${{ matrix.python-version }} id: setuppy uses: actions/setup-python@v5 From d7ddfc3cf018f5f824a1bf0c94d69ebe3ed1dbe4 Mon Sep 17 00:00:00 2001 From: Jose Tiago Macara Coutinho Date: Fri, 2 Aug 2024 08:18:37 +0200 Subject: [PATCH 5/5] Fix to support python 3.9 --- linuxpy/video/device.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linuxpy/video/device.py b/linuxpy/video/device.py index 4dde4ed..75b1f2d 100644 --- a/linuxpy/video/device.py +++ b/linuxpy/video/device.py @@ -1129,7 +1129,8 @@ def __repr__(self): repr += f" {addrepr}" if self.flags: - repr += " flags=" + ",".join(flag.name.lower() for flag in self.flags) + flags = [flag.name.lower() for flag in ControlFlag if ((self._info.flags & flag) == flag)] + repr += " flags=" + ",".join(flags) return f"<{type(self).__name__} {repr}>"