├── .ci.yml ├── .github ├── check_code_formatting.sh ├── save_examples.py └── workflows │ └── python-ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── pyrav4l2 ├── __init__.py ├── controls.py ├── device.py ├── stream.py └── v4l2.py └── setup.py /.ci.yml: -------------------------------------------------------------------------------- 1 | image: ubuntu:rolling 2 | stages: 3 | - .pre 4 | - test 5 | 6 | before_script: 7 | - apt-get update 8 | - DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata 9 | - apt-get install python3 python3-pip git libsm6 libxext6 ffmpeg -y 10 | - python3 -m pip install --upgrade pip 11 | - python3 -m pip install . 12 | - python3 -m pip install yapf pytest pytest-cov 13 | 14 | Lint-with-yapf: 15 | stage: test 16 | script: 17 | - ./.github/check_code_formatting.sh 18 | -------------------------------------------------------------------------------- /.github/check_code_formatting.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ -d pyrav4l2 ]; then python3 -m yapf -ipr pyrav4l2/; fi 5 | test $(git status --porcelain | wc -l) -eq 0 || { git diff; false; } 6 | -------------------------------------------------------------------------------- /.github/save_examples.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import sys 3 | from tuttest import get_snippets 4 | 5 | if __name__ == "__main__": 6 | snippets = get_snippets(sys.argv[1]) 7 | 8 | examples_path = Path("examples") 9 | examples_path.mkdir(parents=True, exist_ok=True) 10 | 11 | for snippet_name in snippets.keys(): 12 | snippet = snippets[snippet_name] 13 | 14 | if snippet.lang == "python" and "unnamed" not in snippet_name: 15 | with open(examples_path / Path(f"{snippet_name}.py"), "w") as f: 16 | f.write(snippet.text) 17 | -------------------------------------------------------------------------------- /.github/workflows/python-ci.yml: -------------------------------------------------------------------------------- 1 | name: Running Python CI jobs 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'pyrav4l2/**' 7 | - 'README.md' 8 | 9 | pull_request: 10 | paths: 11 | - 'pyrav4l2/**' 12 | - 'README.md' 13 | workflow_dispatch: 14 | 15 | jobs: 16 | lint-with-yapf: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Get repository 21 | uses: actions/checkout@v3 22 | 23 | - name: Set up Python environment 24 | uses: actions/setup-python@v4 25 | with: 26 | python-version: '3.9' 27 | 28 | - name: Install Python dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install . 32 | pip install yapf pytest pytest-cov 33 | - name: Lint with yapf 34 | run: | 35 | ./.github/check_code_formatting.sh 36 | 37 | run-example-scripts-from-readme: 38 | runs-on: ubuntu-latest 39 | 40 | steps: 41 | - uses: actions/checkout@v3 42 | 43 | - name: Install tuttest 44 | run: pip install git+https://github.com/antmicro/tuttest.git 45 | 46 | - name: Extract example scripts from README 47 | run: python ./.github/save_examples.py README.md 48 | 49 | - name: Copy examples and pyrav4l2 to shared-dir 50 | run: | 51 | mkdir -p tests/pyrav4l2 52 | cp -r ./pyrav4l2 setup.py ./tests/pyrav4l2 53 | cp -r ./examples ./tests 54 | 55 | - name: Run scripts in Renode 56 | uses: antmicro/renode-linux-runner-action@v1 57 | with: 58 | shared-dirs: ./tests 59 | renode-run: | 60 | pip install ./pyrav4l2 61 | for script in ./examples/*.py; do python $script; done 62 | devices: vivid 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | build/ 3 | dist/ 4 | *.egg-info/ 5 | 6 | .pytest_cache/ 7 | .coverage 8 | 9 | .env 10 | .venv 11 | env/ 12 | venv/ 13 | ENV/ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyrav4l2 - Pythonic, Really Awesome V4L2 utility 2 | 3 | Copyright (c) 2022 [Antmicro](https://www.antmicro.com) 4 | 5 | pyrav4l2 is a library that lets you easily control V4L2 devices inside python scripts. 6 | It provides a high level API for controlling camera parameters and streaming frames. 7 | 8 | # Installation 9 | `pip install git+https://github.com/antmicro/pyrav4l2.git` 10 | 11 | # Examples 12 | ## Creating device 13 | You can create a device instance using the path of the V4L2 device: 14 | 15 | ```python 16 | from pyrav4l2 import Device 17 | 18 | dev = Device("/dev/video0") 19 | ``` 20 | 21 | or by using its ID number: 22 | 23 | ```python 24 | from pyrav4l2 import Device 25 | 26 | dev = Device.with_id(0) 27 | ``` 28 | 29 | ## Getting information about the device 30 | You can easily get device and driver names and check if the selected device supports video capturing 31 | 32 | ```python 33 | from pyrav4l2 import Device 34 | 35 | dev = Device("/dev/video0") 36 | print(f"Device name: {dev.device_name}") 37 | print(f"Driver name: {dev.driver_name}") 38 | if dev.is_video_capture_capable: 39 | print(f"Device supports video capturing") 40 | else: 41 | print(f"Device does not support video capturing") 42 | ``` 43 | 44 | ## Getting information about currently used color format and frame size 45 | This example shows you how to check what color format and frame size is currently used by the device 46 | 47 | ```python 48 | from pyrav4l2 import Device 49 | 50 | dev = Device("/dev/video0") 51 | color_format, frame_size = dev.get_format() 52 | print(f"Color format: {color_format}") 53 | print(f"Frame size: {frame_size}") 54 | ``` 55 | 56 | ## Frame format enumeration and configuration 57 | Here you can see how to get all supported color formats and frame sizes, and configure the device to use the first one of each. 58 | 59 | ```python 60 | from pyrav4l2 import Device 61 | 62 | dev = Device("/dev/video0") 63 | available_formats = dev.available_formats 64 | 65 | for color_format in available_formats.keys(): 66 | print(f"{color_format}:") 67 | for frame_size in available_formats[color_format]: 68 | print(f" {frame_size}") 69 | print() 70 | 71 | color_format = list(available_formats.keys())[0] 72 | frame_size = available_formats[color_format][0] 73 | dev.set_format(color_format, frame_size) 74 | ``` 75 | 76 | ## Device controls enumeration and configuration 77 | This example shows how to get a list of supported controls and iterate over it. 78 | It prints the names of all controls and resets their values to the default ones. 79 | 80 | ```python 81 | from pyrav4l2 import Device 82 | 83 | dev = Device("/dev/video0") 84 | available_controls = dev.controls 85 | 86 | for control in available_controls: 87 | print(control.name) 88 | dev.reset_control_to_default(control) 89 | ``` 90 | 91 | ## Streaming frames 92 | Here you can see how to stream frame from the camera. 93 | The script below captures 10 frames and prints their lengths. 94 | 95 | ```python 96 | from pyrav4l2 import Device, Stream 97 | 98 | dev = Device("/dev/video0") 99 | for (i, frame) in enumerate(Stream(dev)): 100 | print(f"Frame {i}: {len(frame)} bytes") 101 | 102 | if i >= 9: 103 | break 104 | ``` 105 | 106 | # License 107 | [Apache 2.0](LICENSE) 108 | -------------------------------------------------------------------------------- /pyrav4l2/__init__.py: -------------------------------------------------------------------------------- 1 | from .v4l2 import * 2 | from .device import * 3 | from .stream import * 4 | -------------------------------------------------------------------------------- /pyrav4l2/controls.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from abc import ABC 3 | from typing import Type, List 4 | from .v4l2 import * 5 | 6 | 7 | class Control: 8 | 9 | def __init__(self, control: v4l2_query_ext_ctrl) -> None: 10 | self.id = control.id 11 | self.type = control.type 12 | self.name = control.name.decode() 13 | self.minimum = control.minimum 14 | self.maximum = control.maximum 15 | self.step = control.step 16 | self.default_value = control.default_value 17 | self.flags = control.flags 18 | 19 | def __eq__(self, other: Type[Control]) -> bool: 20 | return self.id == other.id 21 | 22 | @property 23 | def is_disabled(self) -> bool: 24 | return bool(self.flags & V4L2_CTRL_FLAG_DISABLED) 25 | 26 | @property 27 | def is_read_only(self) -> bool: 28 | return bool(self.flags & V4L2_CTRL_FLAG_READ_ONLY) 29 | 30 | @property 31 | def is_inactive(self) -> bool: 32 | return bool(self.flags & V4L2_CTRL_FLAG_INACTIVE) 33 | 34 | @property 35 | def is_volatile(self) -> bool: 36 | return bool(self.flags & V4L2_CTRL_FLAG_VOLATILE) 37 | 38 | 39 | class Menu(Control): 40 | 41 | def __init__(self, control: v4l2_query_ext_ctrl, 42 | items: List[Type[Item]]) -> None: 43 | super().__init__(control) 44 | self.items = items 45 | 46 | 47 | class Item(ABC): 48 | 49 | def __init__(self, ctrl_id: int, index: int) -> None: 50 | self.ctrl_id = ctrl_id 51 | self.index = index 52 | 53 | def __eq__(self, other: Type[Item]) -> bool: 54 | return self.ctrl_id == other.ctrl_id and self.index == other.index 55 | 56 | 57 | class MenuItem(Item): 58 | 59 | def __init__(self, ctrl_id: int, index: int, name: str) -> None: 60 | super().__init__(ctrl_id, index) 61 | self.name = name 62 | 63 | 64 | class IntegerMenuItem(Item): 65 | 66 | def __init__(self, ctrl_id: int, index: int, value: int) -> None: 67 | super().__init__(ctrl_id, index) 68 | self.value = value 69 | -------------------------------------------------------------------------------- /pyrav4l2/device.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from pathlib import Path 3 | from typing import Dict, List, Tuple, Type 4 | from fcntl import ioctl 5 | from .controls import Control, Menu, MenuItem, IntegerMenuItem, Item 6 | from .v4l2 import * 7 | 8 | 9 | class ColorFormat: 10 | 11 | def __init__(self, description: str, pixelformat: int, flags: int) -> None: 12 | self.description = description 13 | self.pixelformat = pixelformat 14 | self._flags = flags 15 | 16 | def __str__(self) -> str: 17 | return self.description 18 | 19 | def __eq__(self, other) -> bool: 20 | return self.pixelformat == other.pixelformat 21 | 22 | def __hash__(self): 23 | return hash(self.pixelformat) 24 | 25 | @property 26 | def is_compressed(self) -> bool: 27 | return bool(self._flags & V4L2_FMT_FLAG_COMPRESSED) 28 | 29 | @property 30 | def is_emulated(self) -> bool: 31 | return bool(self._flags & V4L2_FMT_FLAG_EMULATED) 32 | 33 | 34 | class FrameSize: 35 | 36 | def __init__(self, width: int, height: int) -> None: 37 | self.width = width 38 | self.height = height 39 | 40 | def __str__(self) -> str: 41 | return f"{self.width} x {self.height}" 42 | 43 | def __eq__(self, other) -> bool: 44 | return self.width == other.width and self.height == other.height 45 | 46 | 47 | class FrameInterval: 48 | 49 | def __init__(self, numerator: int = 0, denominator: int = 0) -> None: 50 | self.numerator = numerator 51 | self.denominator = denominator 52 | 53 | def __eq__(self, other: FrameInterval) -> bool: 54 | return self.numerator == other.numerator and self.denominator == other.denominator 55 | 56 | def __str__(self) -> str: 57 | if self.denominator != 0: 58 | return str(self.numerator / self.denominator) 59 | else: 60 | return "0.0" 61 | 62 | def __float__(self) -> float: 63 | if self.denominator != 0: 64 | return self.numerator / self.denominator 65 | else: 66 | return 0.0 67 | 68 | 69 | class Device: 70 | """Class representing a v4l2 device""" 71 | 72 | def __init__(self, path: str | Path) -> None: 73 | """ 74 | Parameters 75 | ---------- 76 | path : str | Path 77 | The path to v4l2 device 78 | """ 79 | 80 | self.path = Path(path) 81 | if not self.path.is_char_device(): 82 | raise AttributeError("Provided path is not a device") 83 | 84 | self._get_capabilities() 85 | 86 | if self.is_video_capture_capable: 87 | self._get_available_formats() 88 | 89 | self._get_controls() 90 | 91 | @classmethod 92 | def with_id(self, id: int) -> Device: 93 | """ 94 | Constructs Device instance based on device's id 95 | 96 | Parameters 97 | ---------- 98 | id : int 99 | The id number of v4l2 device 100 | """ 101 | 102 | return Device(f"/dev/video{id}") 103 | 104 | def get_format(self) -> Tuple[ColorFormat, FrameSize]: 105 | """ 106 | Get currently used color format and frame size 107 | 108 | Raises 109 | ------ 110 | DeviceNotSupportVideoCapture 111 | If this device does not support video capture 112 | """ 113 | 114 | if not self.is_video_capture_capable: 115 | raise DeviceNotSupportVideoCapture(self.path) 116 | 117 | with open(self.path) as f_cam: 118 | fmt = v4l2_format() 119 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 120 | ioctl(f_cam, VIDIOC_G_FMT, fmt) 121 | 122 | frame_size = FrameSize(fmt.fmt.pix.width, fmt.fmt.pix.height) 123 | color_format = next( 124 | filter(lambda x: x.pixelformat == fmt.fmt.pix.pixelformat, 125 | self._available_formats.keys())) 126 | 127 | return (color_format, frame_size) 128 | 129 | def set_format(self, color_format: ColorFormat, 130 | frame_size: FrameSize) -> None: 131 | """ 132 | Set color format and frame size 133 | 134 | Parameters 135 | ---------- 136 | color_format : ColorFormat 137 | Color format to be used 138 | frame_size : FrameSize 139 | Frame size to be used 140 | 141 | Raises 142 | ------ 143 | DeviceNotSupportVideoCapture 144 | If this device does not support video capture 145 | UnsupportedColorFormat 146 | If given color format is not supported by the device 147 | UnsupportedFrameSize 148 | If given frame size is not supported by the device 149 | """ 150 | 151 | if not self.is_video_capture_capable: 152 | raise DeviceNotSupportVideoCapture(self.path) 153 | 154 | if any(color_format == available_format 155 | for available_format in self._available_formats.keys()): 156 | if any(frame_size == available_size for available_size in 157 | self._available_formats[color_format]): 158 | with open(self.path) as f_cam: 159 | fmt = v4l2_format() 160 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 161 | ioctl(f_cam, VIDIOC_G_FMT, fmt) 162 | 163 | fmt.fmt.pix.width = frame_size.width 164 | fmt.fmt.pix.height = frame_size.height 165 | fmt.fmt.pix.pixelformat = color_format.pixelformat 166 | fmt.fmt.pix.field = V4L2_FIELD_ANY 167 | fmt.fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT 168 | fmt.fmt.pix.bytesperline = 0 169 | fmt.fmt.pix.sizeimage = 0 170 | ioctl(f_cam, VIDIOC_S_FMT, fmt) 171 | else: 172 | raise UnsupportedFrameSize(self.path, color_format, frame_size) 173 | else: 174 | raise UnsupportedColorFormat(self.path, color_format) 175 | 176 | def set_control_value(self, control: Type[Control], 177 | value: bool | int | str | Type[Item]) -> None: 178 | """ 179 | Set control value 180 | 181 | Parameters 182 | ---------- 183 | control : Type[Control] 184 | Control which value need to be set 185 | value : bool | int | str | Type[Item] 186 | New control value 187 | 188 | Raises 189 | ------ 190 | UnsupportedControl 191 | If device does not have control with given id 192 | WrongValueType 193 | If given type of given value is not supported by control 194 | UnsupportedMenuItem 195 | If menu control does not have given item 196 | WrongIntValue 197 | If value is not in the range supported by control 198 | WrongStringValue 199 | If length of value is not in the range supported by control 200 | """ 201 | 202 | if any(control == available_ctrl for available_ctrl in self._controls): 203 | if isinstance(control, Menu): 204 | if isinstance(value, Item): 205 | if any(item.index == value.index 206 | for item in control.items): 207 | self._set_value(control, value) 208 | else: 209 | raise UnsupportedMenuItem(control, value) 210 | else: 211 | raise WrongValueType(value) 212 | else: 213 | if control.type in [ 214 | V4L2_CTRL_TYPE_INTEGER, V4L2_CTRL_TYPE_INTEGER64 215 | ]: 216 | if type(value) == int: 217 | if value >= control.minimum and value <= control.maximum and ( 218 | control.minimum - value) % control.step == 0: 219 | self._set_value(control, value) 220 | else: 221 | raise WrongIntValue(control, value) 222 | else: 223 | raise WrongValueType(value) 224 | elif control.type in [ 225 | V4L2_CTRL_TYPE_BOOLEAN, V4L2_CTRL_TYPE_BUTTON 226 | ]: 227 | if type(value) == bool: 228 | self._set_value(control, value) 229 | else: 230 | raise WrongValueType(value) 231 | elif control.type == V4L2_CTRL_TYPE_BITMASK: 232 | if type(value) == int: 233 | self._set_value(control, value) 234 | else: 235 | raise WrongValueType(value) 236 | elif control.type == V4L2_CTRL_TYPE_STRING: 237 | if type(value) == str: 238 | if len(value) >= control.minimum and len( 239 | value) <= control.maximum and ( 240 | control.minimum - 241 | len(value)) % control.step == 0: 242 | self._set_value(control, value) 243 | else: 244 | WrongStringValue(control, value) 245 | else: 246 | raise WrongValueType(value) 247 | else: 248 | raise UnsupportedControl(self.path, control) 249 | 250 | def get_control_value( 251 | self, control: Type[Control]) -> bool | int | str | Type[Item]: 252 | """ 253 | Get current control's value 254 | 255 | Parameters 256 | ---------- 257 | control : Type[Control] 258 | Control which value shoulde be returned 259 | 260 | Raises 261 | ------ 262 | UnsupportedControl 263 | If device does not have control with given id 264 | """ 265 | 266 | if any(control == available_ctrl for available_ctrl in self._controls): 267 | ctrl = self._get_control_value(control) 268 | 269 | if control.type in [ 270 | V4L2_CTRL_TYPE_MENU, V4L2_CTRL_TYPE_INTEGER_MENU 271 | ]: 272 | return next( 273 | filter(lambda x: x.index == ctrl.value, control.items)) 274 | elif control.type in [ 275 | V4L2_CTRL_TYPE_BITMASK, V4L2_CTRL_TYPE_INTEGER64 276 | ]: 277 | return ctrl.value64 278 | elif control.type == V4L2_CTRL_TYPE_STRING: 279 | return ctrl.string.decode() 280 | else: 281 | return ctrl.value 282 | else: 283 | raise UnsupportedControl(self.path, control) 284 | 285 | def reset_control_to_default(self, control: Type[Control]) -> None: 286 | """ 287 | Reset control's value to the default one 288 | 289 | Parameters 290 | ---------- 291 | control : Type[Control] 292 | 293 | Raises 294 | ------ 295 | UnsupportedControl 296 | If device does not have control with given id 297 | """ 298 | 299 | if any(control == available_ctrl for available_ctrl in self._controls): 300 | if control.type in [ 301 | V4L2_CTRL_TYPE_INTEGER, V4L2_CTRL_TYPE_BOOLEAN, 302 | V4L2_CTRL_TYPE_BITMASK, V4L2_CTRL_TYPE_MENU, 303 | V4L2_CTRL_TYPE_INTEGER_MENU 304 | ]: 305 | default_value = control.default_value 306 | if control.type == V4L2_CTRL_TYPE_BOOLEAN: 307 | default_value = bool(default_value) 308 | elif control.type in [ 309 | V4L2_CTRL_TYPE_MENU, V4L2_CTRL_TYPE_INTEGER_MENU 310 | ]: 311 | default_value = next( 312 | filter(lambda x: x.index == default_value, 313 | control.items)) 314 | self._set_value(control, default_value) 315 | else: 316 | raise UnsupportedControl(self.path, control) 317 | 318 | def update_control(self, control: Type[Control]) -> Type[Control]: 319 | """ 320 | Updates given control's state 321 | 322 | Parameters 323 | ---------- 324 | control : Type[Control] 325 | 326 | Raises 327 | ------ 328 | UnsupportedControl 329 | If device does not have control with given id 330 | """ 331 | 332 | if not any(control == available_ctrl 333 | for available_ctrl in self._controls): 334 | raise UnsupportedControl(self.path, control) 335 | 336 | with open(self.path) as f_cam: 337 | ctrl = v4l2_query_ext_ctrl() 338 | ctrl.id = control.id 339 | ioctl(f_cam, VIDIOC_QUERY_EXT_CTRL, ctrl) 340 | 341 | if ctrl.type in [V4L2_CTRL_TYPE_MENU, V4L2_CTRL_TYPE_INTEGER_MENU]: 342 | items = [] 343 | for i in range(ctrl.minimum, ctrl.maximum + 1): 344 | menu = v4l2_querymenu() 345 | menu.id = ctrl.id 346 | menu.index = i 347 | 348 | try: 349 | ioctl(f_cam, VIDIOC_QUERYMENU, menu) 350 | except OSError: 351 | continue 352 | 353 | if ctrl.type == V4L2_CTRL_TYPE_MENU: 354 | items.append(MenuItem(ctrl.id, i, menu.name.decode())) 355 | else: 356 | items.append(IntegerMenuItem(ctrl.id, i, menu.value)) 357 | ctrl = Menu(ctrl, items) 358 | ctrl_idx = next( 359 | iter([ 360 | idx for idx, el in enumerate(self._controls) 361 | if el == ctrl 362 | ]), -1) 363 | self._controls[ctrl_idx] = ctrl 364 | return ctrl 365 | else: 366 | ctrl = Control(ctrl) 367 | ctrl_idx = next( 368 | iter([ 369 | idx for idx, el in enumerate(self._controls) 370 | if el == ctrl 371 | ]), -1) 372 | self._controls[ctrl_idx] = ctrl 373 | return ctrl 374 | 375 | def get_available_frame_intervals( 376 | self, color_format: ColorFormat, 377 | frame_size: FrameSize) -> List[FrameInterval]: 378 | """ 379 | Get available fram intervals for given color format and frame size 380 | 381 | Parameters 382 | ---------- 383 | color_format : ColorFormat 384 | Chosen color format 385 | frame_size : FrameSize 386 | Chosen frame size 387 | 388 | Raises 389 | ----- 390 | DeviceNotSupportVideoCapture 391 | If this device does not support video capture 392 | UnsupportedColorFormat 393 | If given color format is not supported by the device 394 | UnsupportedFrameSize 395 | If given frame size is not supported by the device 396 | """ 397 | 398 | if not self.is_video_capture_capable: 399 | raise DeviceNotSupportVideoCapture(self.path) 400 | if not any(color_format == available_format 401 | for available_format in self._available_formats.keys()): 402 | raise UnsupportedColorFormat(self.path, color_format) 403 | if not any( 404 | frame_size == available_size 405 | for available_size in self._available_formats[color_format]): 406 | raise UnsupportedFrameSize(self.path, color_format, frame_size) 407 | 408 | intervals = [] 409 | with open(self.path) as f_cam: 410 | frmival = v4l2_frmivalenum() 411 | frmival.pixel_format = color_format.pixelformat 412 | frmival.width = frame_size.width 413 | frmival.height = frame_size.height 414 | 415 | while True: 416 | try: 417 | ioctl(f_cam, VIDIOC_ENUM_FRAMEINTERVALS, frmival) 418 | except OSError: 419 | break 420 | 421 | frmival.index += 1 422 | if frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE: 423 | intervals.append( 424 | FrameInterval(frmival.discrete.numerator, 425 | frmival.discrete.denominator)) 426 | 427 | return intervals 428 | 429 | def set_frame_interval(self, interval: FrameInterval) -> None: 430 | """ 431 | Set frame interval 432 | 433 | Parameters 434 | ---------- 435 | interval : FrameInterval 436 | 437 | Raises 438 | ------ 439 | DeviceNotSupportVideoCapture 440 | If this device does not support video capture 441 | WrongFrameInterval 442 | If given interval is not supported with currently used color format and frame size 443 | """ 444 | 445 | if not self.is_video_capture_capable: 446 | raise DeviceNotSupportVideoCapture(self.path) 447 | 448 | color_format, frame_size = self.get_format() 449 | available_intervals = self.get_available_frame_intervals( 450 | color_format, frame_size) 451 | 452 | if not any(interval == available_interval 453 | for available_interval in available_intervals): 454 | raise WrongFrameInterval(interval, color_format, frame_size) 455 | 456 | with open(self.path) as f_cam: 457 | streamparm = v4l2_streamparm() 458 | streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 459 | ioctl(f_cam, VIDIOC_G_PARM, streamparm) 460 | 461 | streamparm.parm.capture.timeperframe.numerator = interval.numerator 462 | streamparm.parm.capture.timeperframe.denominator = interval.denominator 463 | ioctl(f_cam, VIDIOC_S_PARM, streamparm) 464 | 465 | def get_frame_interval(self) -> FrameInterval: 466 | if not self.is_video_capture_capable: 467 | raise DeviceNotSupportVideoCapture(self.path) 468 | 469 | interval = FrameInterval() 470 | with open(self.path) as f_cam: 471 | streamparm = v4l2_streamparm() 472 | streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 473 | ioctl(f_cam, VIDIOC_G_PARM, streamparm) 474 | 475 | interval.numerator = streamparm.parm.capture.timeperframe.numerator 476 | interval.denominator = streamparm.parm.capture.timeperframe.denominator 477 | 478 | return interval 479 | 480 | @property 481 | def driver_name(self) -> str: 482 | return self._driver 483 | 484 | @property 485 | def device_name(self) -> str: 486 | return self._card 487 | 488 | @property 489 | def is_video_capture_capable(self) -> bool: 490 | return bool(self._capabilities & V4L2_CAP_VIDEO_CAPTURE) 491 | 492 | @property 493 | def available_formats(self) -> Dict[ColorFormat, List[FrameSize]]: 494 | if not self.is_video_capture_capable: 495 | raise DeviceNotSupportVideoCapture(self.path) 496 | 497 | return self._available_formats 498 | 499 | @property 500 | def controls(self) -> List[Type[Control]]: 501 | return self._controls 502 | 503 | def _set_value(self, control: Type[Control], 504 | value: bool | int | str | Type[Item]) -> None: 505 | with open(self.path) as f_cam: 506 | ctrl = v4l2_ext_control() 507 | ctrl.id = control.id 508 | if control.type in [ 509 | V4L2_CTRL_TYPE_MENU, V4L2_CTRL_TYPE_INTEGER_MENU 510 | ]: 511 | ctrl.value = value.index 512 | elif control.type in [ 513 | V4L2_CTRL_TYPE_BITMASK, V4L2_CTRL_TYPE_INTEGER64 514 | ]: 515 | ctrl.value64 = value 516 | elif control.type == V4L2_CTRL_TYPE_STRING: 517 | ctrl.string = value 518 | ctrl.size = len(value) + 1 519 | else: 520 | ctrl.value = value 521 | 522 | ectrls = v4l2_ext_controls() 523 | ectrls.count = 1 524 | ectrls.controls = ctypes.pointer(ctrl) 525 | 526 | try: 527 | ioctl(f_cam, VIDIOC_S_EXT_CTRLS, ectrls) 528 | except OSError: 529 | pass 530 | 531 | def _get_control_value(self, control: Type[Control]) -> v4l2_ext_control: 532 | ctrl = v4l2_ext_control() 533 | ctrl.id = control.id 534 | if control.type == V4L2_CTRL_TYPE_STRING: 535 | ctrl.size = control.maximum + 1 536 | ctrl.string = bytes(control.maximum + 1) 537 | 538 | ectrls = v4l2_ext_controls() 539 | ectrls.controls = ctypes.pointer(ctrl) 540 | ectrls.count = 1 541 | 542 | with open(self.path) as f_cam: 543 | try: 544 | ioctl(f_cam, VIDIOC_G_EXT_CTRLS, ectrls) 545 | except OSError: 546 | pass 547 | 548 | return ctrl 549 | 550 | def _get_capabilities(self) -> None: 551 | with open(self.path) as f_cam: 552 | caps = v4l2_capability() 553 | ioctl(f_cam, VIDIOC_QUERYCAP, caps) 554 | 555 | self._driver = caps.driver.decode() 556 | self._card = caps.card.decode() 557 | self._capabilities = caps.device_caps 558 | 559 | def _get_available_formats(self) -> None: 560 | if not self.is_video_capture_capable: 561 | raise DeviceNotSupportVideoCapture(self.path) 562 | 563 | self._available_formats = {} 564 | 565 | with open(self.path) as f_cam: 566 | fmt = v4l2_fmtdesc() 567 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 568 | 569 | while True: 570 | try: 571 | ioctl(f_cam, VIDIOC_ENUM_FMT, fmt) 572 | except OSError: 573 | break 574 | 575 | color_format = ColorFormat(fmt.description.decode(), 576 | fmt.pixelformat, fmt.flags) 577 | self._available_formats[color_format] = [] 578 | 579 | frmsize = v4l2_frmsizeenum() 580 | frmsize.pixel_format = color_format.pixelformat 581 | 582 | while True: 583 | try: 584 | ioctl(f_cam, VIDIOC_ENUM_FRAMESIZES, frmsize) 585 | except OSError: 586 | break 587 | 588 | if frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE: 589 | self._available_formats[color_format].append( 590 | FrameSize(frmsize.discrete.width, 591 | frmsize.discrete.height)) 592 | 593 | frmsize.index += 1 594 | 595 | fmt.index += 1 596 | 597 | def _get_controls(self) -> None: 598 | self._controls = [] 599 | with open(self.path) as f_cam: 600 | ctrl_id = V4L2_CTRL_FLAG_NEXT_CTRL 601 | 602 | while True: 603 | ctrl = v4l2_query_ext_ctrl() 604 | ctrl.id = ctrl_id 605 | try: 606 | ioctl(f_cam, VIDIOC_QUERY_EXT_CTRL, ctrl) 607 | except OSError: 608 | break 609 | 610 | if ctrl.type in [ 611 | V4L2_CTRL_TYPE_MENU, V4L2_CTRL_TYPE_INTEGER_MENU 612 | ]: 613 | items = [] 614 | for i in range(ctrl.minimum, ctrl.maximum + 1): 615 | menu = v4l2_querymenu() 616 | menu.id = ctrl.id 617 | menu.index = i 618 | 619 | try: 620 | ioctl(f_cam, VIDIOC_QUERYMENU, menu) 621 | except OSError: 622 | continue 623 | 624 | if ctrl.type == V4L2_CTRL_TYPE_MENU: 625 | items.append( 626 | MenuItem(ctrl.id, i, menu.name.decode())) 627 | else: 628 | items.append( 629 | IntegerMenuItem(ctrl.id, i, menu.value)) 630 | self._controls.append(Menu(ctrl, items)) 631 | else: 632 | self._controls.append(Control(ctrl)) 633 | 634 | ctrl_id = ctrl.id | V4L2_CTRL_FLAG_NEXT_CTRL 635 | 636 | 637 | class DeviceNotSupportVideoCapture(TypeError): 638 | 639 | def __init__(self, device_path: str | Path) -> None: 640 | self.device_path = device_path 641 | 642 | def __str__(self) -> str: 643 | return f"Device '{self.device_path}' is not video capture capable" 644 | 645 | 646 | class UnsupportedColorFormat(ValueError): 647 | 648 | def __init__(self, device_path: str | Path, 649 | color_format: ColorFormat) -> None: 650 | self.device_path = device_path 651 | self.color_format = color_format 652 | 653 | def __str__(self) -> str: 654 | return f"Device '{self.device_path}' does not support '{self.color_format}' color format" 655 | 656 | 657 | class UnsupportedFrameSize(ValueError): 658 | 659 | def __init__(self, device_path: str | Path, color_format: ColorFormat, 660 | frame_size: FrameSize) -> None: 661 | self.device_path = device_path 662 | self.color_format = color_format 663 | self.frame_size = frame_size 664 | 665 | def __str__(self) -> str: 666 | return f"Device '{self.device_path}' does not support '{self.frame_size}' frame size for '{self.color_format}' format" 667 | 668 | 669 | class UnsupportedControl(ValueError): 670 | 671 | def __init__(self, device_path: str | Path, 672 | control: Type[Control]) -> None: 673 | self.device_path = device_path 674 | self.control = control 675 | 676 | def __str__(self) -> str: 677 | return f"Device '{self.device_path}' does not support control with id '{self.control.id}' ('{self.control.name}')" 678 | 679 | 680 | class UnsupportedMenuItem(ValueError): 681 | 682 | def __init__(self, menu: Menu, item: Type[Item]) -> None: 683 | self.menu = menu 684 | self.item = item 685 | 686 | def __str__(self) -> str: 687 | if isinstance(self.item, MenuItem): 688 | return f"Menu '{self.menu.name}' does not support '{self.item.name}' menu item" 689 | else: 690 | return f"Menu '{self.menu.name}' does not support '{self.item.value}' menu item" 691 | 692 | 693 | class WrongValueType(TypeError): 694 | 695 | def __init__(self, value: bool | int | str | Type[Item]) -> None: 696 | self.value = value 697 | 698 | def __str__(self) -> str: 699 | return f"Value has wrong type: '{type(self.value).__name__}'" 700 | 701 | 702 | class WrongIntValue(ValueError): 703 | 704 | def __init__(self, control: Control, value: int) -> None: 705 | self.control = control 706 | self.value = value 707 | 708 | def __str__(self) -> None: 709 | return f"'{self.value}' is not valid for '{self.control.name}'. Allowed values: {self.control.minimum} - {self.control.maximum} (step: {self.control.step})" 710 | 711 | 712 | class WrongStringValue(ValueError): 713 | 714 | def __init__(self, control: Control, value: str) -> None: 715 | self.control = control 716 | self.value = value 717 | 718 | def __str__(self) -> str: 719 | return f"'{self.value}' is not a valid string for '{self.control.name}'. Its length should be equal to {self.control.minimum} - {self.control.maximum} (step: {self.control.step}), but it is {len(self.value)}" 720 | 721 | 722 | class WrongFrameInterval(ValueError): 723 | 724 | def __init__(self, interval: FrameInterval, color_format: ColorFormat, 725 | frame_size: FrameSize) -> None: 726 | self.interval = interval 727 | self.color_format = color_format 728 | self.frame_size = frame_size 729 | 730 | def __str__(self) -> str: 731 | return f"{self.interval.numerator} / {self.interval.denominator} ({self.interval.denominator / self.interval.numerator} fps) frame interval is not supported for {self.color_format} color format and {self.frame_size} frame size" 732 | -------------------------------------------------------------------------------- /pyrav4l2/stream.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from fcntl import ioctl 3 | import mmap 4 | from select import select 5 | 6 | from .device import Device 7 | from .v4l2 import * 8 | 9 | 10 | class Stream: 11 | """ 12 | Class that uses Device for capturing frames. 13 | When iterating over Stream object, it returns newly captured frame every iteration 14 | """ 15 | 16 | def __init__(self, device: Device) -> None: 17 | """ 18 | Parameters 19 | ---------- 20 | device : Device 21 | Device that should be used for streaming 22 | 23 | Raises 24 | ------ 25 | IOError 26 | If there is not enough memory for a buffer 27 | """ 28 | 29 | self._context_level = 0 30 | self.device = device 31 | 32 | self._open() 33 | 34 | def __iter__(self) -> bytes: 35 | """ 36 | Yields the captured frame every iteration 37 | """ 38 | 39 | if self.f_cam.closed: 40 | self._open() 41 | 42 | ioctl(self.f_cam, VIDIOC_STREAMON, 43 | ctypes.c_int(V4L2_BUF_TYPE_VIDEO_CAPTURE)) 44 | select((self.f_cam, ), (), ()) 45 | 46 | try: 47 | while True: 48 | buf = self.buffers[0][0] 49 | ioctl(self.f_cam, VIDIOC_DQBUF, buf) 50 | 51 | frames = self.buffers[buf.index][1][:buf.bytesused] 52 | ioctl(self.f_cam, VIDIOC_QBUF, buf) 53 | 54 | yield frames 55 | finally: 56 | self._stop() 57 | 58 | def _open(self): 59 | self.f_cam = open(self.device.path, "rb+", buffering=0) 60 | 61 | req = v4l2_requestbuffers() 62 | req.count = 4 63 | req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 64 | req.memory = V4L2_MEMORY_MMAP 65 | ioctl(self.f_cam, VIDIOC_REQBUFS, req) 66 | 67 | if req.count == 0: 68 | raise IOError("Not enough buffer memory") 69 | 70 | self.buffers = [] 71 | for i in range(req.count): 72 | buf = v4l2_buffer() 73 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE 74 | buf.memory = V4L2_MEMORY_MMAP 75 | buf.index = i 76 | ioctl(self.f_cam, VIDIOC_QUERYBUF, buf) 77 | 78 | buffer = mmap.mmap(self.f_cam.fileno(), 79 | length=buf.length, 80 | flags=mmap.MAP_SHARED, 81 | prot=mmap.PROT_READ, 82 | offset=buf.m.offset) 83 | ioctl(self.f_cam, VIDIOC_QBUF, buf) 84 | 85 | self.buffers.append((buf, buffer)) 86 | 87 | def _stop(self) -> None: 88 | ioctl(self.f_cam, VIDIOC_STREAMOFF, 89 | ctypes.c_int(V4L2_BUF_TYPE_VIDEO_CAPTURE)) 90 | for buffer in self.buffers: 91 | buffer[1].close() 92 | self.f_cam.close() 93 | -------------------------------------------------------------------------------- /pyrav4l2/v4l2.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | # 4 | # Enums 5 | # 6 | # v4l2_buf_type 7 | V4L2_BUF_TYPE_VIDEO_CAPTURE = 1 8 | V4L2_BUF_TYPE_VIDEO_OUTPUT = 2 9 | V4L2_BUF_TYPE_VIDEO_OVERLAY = 3 10 | V4L2_BUF_TYPE_VBI_CAPTURE = 4 11 | V4L2_BUF_TYPE_VBI_OUTPUT = 5 12 | V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6 13 | V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7 14 | V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8 15 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9 16 | V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10 17 | V4L2_BUF_TYPE_SDR_CAPTURE = 11 18 | V4L2_BUF_TYPE_SDR_OUTPUT = 12 19 | V4L2_BUF_TYPE_META_CAPTURE = 13 20 | V4L2_BUF_TYPE_META_OUTPUT = 14 21 | 22 | # v4l2_field 23 | V4L2_FIELD_ANY = 0 24 | V4L2_FIELD_NONE = 1 25 | V4L2_FIELD_TOP = 2 26 | V4L2_FIELD_BOTTOM = 3 27 | V4L2_FIELD_INTERLACED = 4 28 | V4L2_FIELD_SEQ_TB = 5 29 | V4L2_FIELD_SEQ_BT = 6 30 | V4L2_FIELD_ALTERNATE = 7 31 | V4L2_FIELD_INTERLACED_TB = 8 32 | V4L2_FIELD_INTERLACED_BT = 9 33 | 34 | # v4l2_colorspace 35 | V4L2_COLORSPACE_DEFAULT = 0 36 | V4L2_COLORSPACE_SMPTE170M = 1 37 | V4L2_COLORSPACE_SMPTE240M = 2 38 | V4L2_COLORSPACE_REC709 = 3 39 | V4L2_COLORSPACE_BT878 = 4 40 | V4L2_COLORSPACE_470_SYSTEM_M = 5 41 | V4L2_COLORSPACE_470_SYSTEM_BG = 6 42 | V4L2_COLORSPACE_JPEG = 7 43 | V4L2_COLORSPACE_SRGB = 8 44 | V4L2_COLORSPACE_OPRGB = 9 45 | V4L2_COLORSPACE_BT2020 = 10 46 | V4L2_COLORSPACE_RAW = 11 47 | V4L2_COLORSPACE_DCI_P3 = 12 48 | 49 | # v4l2_ctrl_type 50 | V4L2_CTRL_TYPE_INTEGER = 1 51 | V4L2_CTRL_TYPE_BOOLEAN = 2 52 | V4L2_CTRL_TYPE_MENU = 3 53 | V4L2_CTRL_TYPE_BUTTON = 4 54 | V4L2_CTRL_TYPE_INTEGER64 = 5 55 | V4L2_CTRL_TYPE_CTRL_CLASS = 6 56 | V4L2_CTRL_TYPE_STRING = 7 57 | V4L2_CTRL_TYPE_BITMASK = 8 58 | V4L2_CTRL_TYPE_INTEGER_MENU = 9 59 | 60 | # Compound types are >= 0x0100 61 | V4L2_CTRL_COMPOUND_TYPES = 0x0100 62 | V4L2_CTRL_TYPE_U8 = 0x0100 63 | V4L2_CTRL_TYPE_U16 = 0x0101 64 | V4L2_CTRL_TYPE_U32 = 0x0102 65 | V4L2_CTRL_TYPE_AREA = 0x0106 66 | 67 | V4L2_CTRL_TYPE_HDR10_CLL_INFO = 0x0110 68 | V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY = 0x0111 69 | 70 | V4L2_CTRL_TYPE_H264_SPS = 0x0200 71 | V4L2_CTRL_TYPE_H264_PPS = 0x0201 72 | V4L2_CTRL_TYPE_H264_SCALING_MATRIX = 0x0202 73 | V4L2_CTRL_TYPE_H264_SLICE_PARAMS = 0x0203 74 | V4L2_CTRL_TYPE_H264_DECODE_PARAMS = 0x0204 75 | V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205 76 | 77 | V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220 78 | 79 | V4L2_CTRL_TYPE_VP8_FRAME = 0x0240 80 | 81 | V4L2_CTRL_TYPE_MPEG2_QUANTISATION = 0x0250 82 | V4L2_CTRL_TYPE_MPEG2_SEQUENCE = 0x0251 83 | V4L2_CTRL_TYPE_MPEG2_PICTURE = 0x0252 84 | 85 | V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR = 0x0260 86 | V4L2_CTRL_TYPE_VP9_FRAME = 0x0261 87 | 88 | # 89 | # Flags 90 | # 91 | 92 | # Values for 'capabilities' field in v4l2_capability 93 | V4L2_CAP_VIDEO_CAPTURE = 0x00000001 # Is a video capture device 94 | V4L2_CAP_VIDEO_OUTPUT = 0x00000002 # Is a video output device 95 | V4L2_CAP_VIDEO_OVERLAY = 0x00000004 # Can do video overlay 96 | V4L2_CAP_VBI_CAPTURE = 0x00000010 # Is a raw VBI capture device 97 | V4L2_CAP_VBI_OUTPUT = 0x00000020 # Is a raw VBI output device 98 | V4L2_CAP_SLICED_VBI_CAPTURE = 0x00000040 # Is a sliced VBI capture device 99 | V4L2_CAP_SLICED_VBI_OUTPUT = 0x00000080 # Is a sliced VBI output device 100 | V4L2_CAP_RDS_CAPTURE = 0x00000100 # RDS data capture 101 | V4L2_CAP_VIDEO_OUTPUT_OVERLAY = 0x00000200 # Can do video output overlay 102 | V4L2_CAP_HW_FREQ_SEEK = 0x00000400 # Can do hardware frequency seek 103 | V4L2_CAP_RDS_OUTPUT = 0x00000800 # Is an RDS encoder 104 | 105 | V4L2_CAP_VIDEO_CAPTURE_MPLANE = 0x00001000 106 | V4L2_CAP_VIDEO_OUTPUT_MPLANE = 0x00002000 107 | V4L2_CAP_VIDEO_M2M_MPLANE = 0x00004000 108 | V4L2_CAP_VIDEO_M2M = 0x00008000 109 | 110 | V4L2_CAP_TUNER = 0x00010000 # has a tuner 111 | V4L2_CAP_AUDIO = 0x00020000 # has audio support 112 | V4L2_CAP_RADIO = 0x00040000 # is a radio device 113 | V4L2_CAP_MODULATOR = 0x00080000 # has a modulator 114 | 115 | V4L2_CAP_SDR_CAPTURE = 0x00100000 # Is a SDR capture device 116 | V4L2_CAP_EXT_PIX_FORMAT = 0x00200000 # Supports the extended pixel format 117 | V4L2_CAP_SDR_OUTPUT = 0x00400000 # Is a SDR output device 118 | V4L2_CAP_META_CAPTURE = 0x00800000 # Is a metadata capture device 119 | 120 | V4L2_CAP_READWRITE = 0x01000000 # read/write systemcalls 121 | V4L2_CAP_ASYNCIO = 0x02000000 # async I/O 122 | V4L2_CAP_STREAMING = 0x04000000 # streaming I/O ioctls 123 | V4L2_CAP_META_OUTPUT = 0x08000000 # Is a metadata output device 124 | 125 | V4L2_CAP_TOUCH = 0x10000000 # Is a touch device 126 | 127 | V4L2_CAP_IO_MC = 0x20000000 # Is input/output controlled by the media controller 128 | 129 | V4L2_CAP_DEVICE_CAPS = 0x80000000 # sets device capabilities field 130 | 131 | # v4l2_fmtdesc flags 132 | V4L2_FMT_FLAG_COMPRESSED = 0x0001 133 | V4L2_FMT_FLAG_EMULATED = 0x0002 134 | V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM = 0x0004 135 | V4L2_FMT_FLAG_DYN_RESOLUTION = 0x0008 136 | V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL = 0x0010 137 | V4L2_FMT_FLAG_CSC_COLORSPACE = 0x0020 138 | V4L2_FMT_FLAG_CSC_XFER_FUNC = 0x0040 139 | V4L2_FMT_FLAG_CSC_YCBCR_ENC = 0x0080 140 | V4L2_FMT_FLAG_CSC_HSV_ENC = V4L2_FMT_FLAG_CSC_YCBCR_ENC 141 | V4L2_FMT_FLAG_CSC_QUANTIZATION = 0x0100 142 | 143 | # Values for type field in v4l2_frmsizeenum 144 | V4L2_FRMSIZE_TYPE_DISCRETE = 1 145 | V4L2_FRMSIZE_TYPE_CONTINUOUS = 2 146 | V4L2_FRMSIZE_TYPE_STEPWISE = 3 147 | 148 | # Control flags 149 | V4L2_CTRL_FLAG_DISABLED = 0x0001 150 | V4L2_CTRL_FLAG_GRABBED = 0x0002 151 | V4L2_CTRL_FLAG_READ_ONLY = 0x0004 152 | V4L2_CTRL_FLAG_UPDATE = 0x0008 153 | V4L2_CTRL_FLAG_INACTIVE = 0x0010 154 | V4L2_CTRL_FLAG_SLIDER = 0x0020 155 | V4L2_CTRL_FLAG_WRITE_ONLY = 0x0040 156 | V4L2_CTRL_FLAG_VOLATILE = 0x0080 157 | V4L2_CTRL_FLAG_HAS_PAYLOAD = 0x0100 158 | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE = 0x0200 159 | V4L2_CTRL_FLAG_MODIFY_LAYOUT = 0x0400 160 | 161 | # Query flags, to be ORed with the control ID 162 | V4L2_CTRL_FLAG_NEXT_CTRL = 0x80000000 163 | V4L2_CTRL_FLAG_NEXT_COMPOUND = 0x40000000 164 | 165 | # User-class control IDs defined by V4L2 166 | V4L2_CID_MAX_CTRLS = 1024 167 | # IDs reserved for driver specific controls 168 | V4L2_CID_PRIVATE_BASE = 0x08000000 169 | 170 | V4L2_CTRL_ID_MASK = 0x0fffffff 171 | 172 | 173 | def V4L2_CTRL_ID2CLASS(id): 174 | return id & 0x0fff0000 175 | 176 | 177 | def V4L2_CTRL_ID2WHICH(id): 178 | return id & 0x0fff0000 179 | 180 | 181 | def V4L2_CTRL_DRIVER_PRIV(id): 182 | return id & 0xffff >= 0x1000 183 | 184 | 185 | V4L2_CTRL_MAX_DIMS = 4 186 | V4L2_CTRL_WHICH_CUR_VAL = 0 187 | V4L2_CTRL_WHICH_DEF_VAL = 0x0f000000 188 | V4L2_CTRL_WHICH_REQUEST_VAL = 0x0f010000 189 | 190 | # v4l2_memory 191 | V4L2_MEMORY_MMAP = 1 192 | V4L2_MEMORY_USERPTR = 2 193 | V4L2_MEMORY_OVERLAY = 3 194 | V4L2_MEMORY_DMABUF = 4 195 | 196 | V4L2_CAP_TIMEPERFRAME = 0x1000 197 | V4L2_MODE_HIGHQUALITY = 0x0001 198 | 199 | V4L2_FRMIVAL_TYPE_DISCRETE = 1 200 | V4L2_FRMIVAL_TYPE_CONTINUOUS = 2 201 | V4L2_FRMIVAL_TYPE_STEPWISE = 3 202 | 203 | 204 | # 205 | # v4l structures 206 | # 207 | class v4l2_pix_format(ctypes.Structure): 208 | 209 | class _encoding(ctypes.Union): 210 | _fields_ = [ 211 | ("ycbcr_enc", ctypes.c_uint32), 212 | ("hsv_enc", ctypes.c_uint32), 213 | ] 214 | 215 | _fields_ = [ 216 | ("width", ctypes.c_uint32), 217 | ("height", ctypes.c_uint32), 218 | ("pixelformat", ctypes.c_uint32), 219 | ("field", ctypes.c_uint32), 220 | ("bytesperline", ctypes.c_uint32), 221 | ("sizeimage", ctypes.c_uint32), 222 | ("colorspace", ctypes.c_uint32), 223 | ("priv", ctypes.c_uint32), 224 | ("flags", ctypes.c_uint32), 225 | ("_", _encoding), 226 | ("quantization", ctypes.c_uint32), 227 | ("xfer_func", ctypes.c_uint32), 228 | ] 229 | 230 | _anonymous_ = ("_", ) 231 | 232 | width = None 233 | height = None 234 | pixelformat = None 235 | field = None 236 | bytesperline = None 237 | sizeimage = None 238 | colorspace = None 239 | priv = None 240 | flags = None 241 | ycbcr_enc = None 242 | hsv_enc = None 243 | quantization = None 244 | xfer_func = None 245 | 246 | 247 | class v4l2_rect(ctypes.Structure): 248 | _fields_ = [ 249 | ("left", ctypes.c_int32), 250 | ("top", ctypes.c_int32), 251 | ("width", ctypes.c_uint32), 252 | ("height", ctypes.c_uint32), 253 | ] 254 | 255 | left = None 256 | top = None 257 | width = None 258 | height = None 259 | 260 | 261 | class v4l2_clip(ctypes.Structure): 262 | c = None 263 | next = None 264 | 265 | 266 | v4l2_clip._fields_ = [("c", v4l2_rect), ("next", ctypes.POINTER(v4l2_clip))] 267 | 268 | 269 | class v4l2_window(ctypes.Structure): 270 | _fields_ = [ 271 | ("w", v4l2_rect), 272 | ("field", ctypes.c_uint32), # v4l2_field 273 | ("chromakey", ctypes.c_uint32), 274 | ("clips", ctypes.POINTER(v4l2_clip)), 275 | ("clipcount", ctypes.c_uint32), 276 | ("bitmap", ctypes.c_void_p), 277 | ("global_alpha", ctypes.c_uint8), 278 | ] 279 | 280 | w = None 281 | field = None 282 | chromakey = None 283 | clips = None 284 | clipcount = None 285 | bitmap = None 286 | global_alpha = None 287 | 288 | 289 | class v4l2_vbi_format(ctypes.Structure): 290 | _fields_ = [ 291 | ("sampling_rate", ctypes.c_uint32), # in Hz 292 | ("offset", ctypes.c_uint32), 293 | ("samples_per_line", ctypes.c_uint32), 294 | ("sample_format", ctypes.c_uint32), # V4L2_PIX_FMT_* 295 | ("start", ctypes.c_int32 * 2), 296 | ("count", ctypes.c_uint32 * 2), 297 | ("flags", ctypes.c_uint32), # V4L2_VBI_* 298 | ("reserved", ctypes.c_uint32 * 2), 299 | ] 300 | 301 | sampling_rate = None 302 | offset = None 303 | samples_per_line = None 304 | sample_format = None 305 | start = None 306 | count = None 307 | flags = None 308 | reserved = None 309 | 310 | 311 | class v4l2_sliced_vbi_format(ctypes.Structure): 312 | _fields_ = [ 313 | ("service_set", ctypes.c_uint16), 314 | ("service_lines", ctypes.c_uint16 * 2 * 24), 315 | ("io_size", ctypes.c_uint32), 316 | ("reserved", ctypes.c_uint32), 317 | ] 318 | 319 | service_set = None 320 | service_lines = None 321 | io_size = None 322 | reserved = None 323 | 324 | 325 | class v4l2_format(ctypes.Structure): 326 | 327 | class _fmt(ctypes.Union): 328 | _fields_ = [ 329 | ("pix", v4l2_pix_format), 330 | ("win", v4l2_window), 331 | ("vbi", v4l2_vbi_format), 332 | ("sliced", v4l2_sliced_vbi_format), 333 | ("raw_data", ctypes.c_char * 200), 334 | ] 335 | 336 | _fields_ = [ 337 | ("type", ctypes.c_uint32), # v4l2_buf_type 338 | ("fmt", _fmt), 339 | ] 340 | 341 | type = None 342 | fmt = None 343 | 344 | 345 | class v4l2_capability(ctypes.Structure): 346 | _fields_ = [ 347 | ("driver", ctypes.c_char * 16), 348 | ("card", ctypes.c_char * 32), 349 | ("bus_info", ctypes.c_char * 32), 350 | ("version", ctypes.c_uint32), 351 | ("capabilities", ctypes.c_uint32), 352 | ("device_caps", ctypes.c_uint32), 353 | ("reserved", ctypes.c_uint32 * 3), 354 | ] 355 | 356 | driver = None 357 | card = None 358 | bus_info = None 359 | version = None 360 | capabilities = None 361 | device_caps = None 362 | reserved = None 363 | 364 | 365 | class v4l2_fmtdesc(ctypes.Structure): 366 | _fields_ = [ 367 | ("index", ctypes.c_uint32), 368 | ("type", ctypes.c_uint32), # v4l2_buf_type 369 | ("flags", ctypes.c_uint32), 370 | ("description", ctypes.c_char * 32), # Description string 371 | ("pixelformat", ctypes.c_uint32), # Format fourcc 372 | ("mbus_code", ctypes.c_uint32), # Media bus code 373 | ("reserved", ctypes.c_uint32 * 3), 374 | ] 375 | 376 | index = None 377 | type = None 378 | flags = None 379 | description = None 380 | pixelformat = None 381 | mbus_code = None 382 | reserved = None 383 | 384 | 385 | class v4l2_control(ctypes.Structure): 386 | _fields_ = [ 387 | ("id", ctypes.c_uint32), 388 | ("value", ctypes.c_int32), 389 | ] 390 | 391 | id = None 392 | value = None 393 | 394 | 395 | class v4l2_ext_control(ctypes.Structure): 396 | 397 | class _anonymous(ctypes.Union): 398 | _fields_ = [ 399 | ("value", ctypes.c_int32), 400 | ("value64", ctypes.c_int64), 401 | ("string", ctypes.c_char_p), 402 | ("p_u8", ctypes.POINTER(ctypes.c_uint8)), 403 | ("p_u16", ctypes.POINTER(ctypes.c_uint16)), 404 | ("p_u32", ctypes.POINTER(ctypes.c_uint32)), 405 | ("ptr", ctypes.c_void_p), 406 | ] 407 | 408 | _fields_ = [ 409 | ("id", ctypes.c_uint32), 410 | ("size", ctypes.c_uint32), 411 | ("reserved2", ctypes.c_uint32), 412 | ("_", _anonymous), 413 | ] 414 | 415 | _anonymous_ = ("_", ) 416 | _pack_ = 1 417 | 418 | id = None 419 | size = None 420 | reserved2 = None 421 | value = None 422 | value64 = None 423 | string = None 424 | p_u8 = None 425 | p_u16 = None 426 | p_u32 = None 427 | ptr = None 428 | 429 | 430 | class v4l2_ext_controls(ctypes.Structure): 431 | 432 | class _anonymous(ctypes.Union): 433 | _fields_ = [ 434 | ("ctrl_class", ctypes.c_uint32), 435 | ("which", ctypes.c_uint32), 436 | ] 437 | 438 | _fields_ = [ 439 | ("_", _anonymous), 440 | ("count", ctypes.c_uint32), 441 | ("error_idx", ctypes.c_uint32), 442 | ("request_fd", ctypes.c_int32), 443 | ("reserved", ctypes.c_uint32), 444 | ("controls", ctypes.POINTER(v4l2_ext_control)), 445 | ] 446 | 447 | _anonymous_ = ("_", ) 448 | 449 | ctrl_class = None 450 | which = None 451 | count = None 452 | error_idx = None 453 | request_fd = None 454 | reserved = None 455 | controls = None 456 | 457 | 458 | class v4l2_frmsize_discrete(ctypes.Structure): 459 | _fields_ = [ 460 | ("width", ctypes.c_uint32), # Frame width [pixel] 461 | ("height", ctypes.c_uint32), # Frame height [pixel] 462 | ] 463 | 464 | width = None 465 | height = None 466 | 467 | 468 | class v4l2_frmsize_stepwise(ctypes.Structure): 469 | _fields_ = [ 470 | ("min_width", ctypes.c_uint32), # Minimum frame width [pixel] 471 | ("max_width", ctypes.c_uint32), # Maximum frame width [pixel] 472 | ("step_width", ctypes.c_uint32), # Frame width step size [pixel] 473 | ("min_height", ctypes.c_uint32), # Minimum frame height [pixel] 474 | ("max_height", ctypes.c_uint32), # Maximum frame height [pixel] 475 | ("step_height", ctypes.c_uint32), # Frame height step size [pixel] 476 | ] 477 | 478 | min_width = None 479 | max_width = None 480 | step_width = None 481 | min_height = None 482 | max_height = None 483 | step_height = None 484 | 485 | 486 | class v4l2_frmsizeenum(ctypes.Structure): 487 | 488 | class _anonymous(ctypes.Union): 489 | _fields_ = [ 490 | ("discrete", v4l2_frmsize_discrete), 491 | ("stepwise", v4l2_frmsize_stepwise), 492 | ] 493 | 494 | _fields_ = [ 495 | ("index", ctypes.c_uint32), # Frame size number 496 | ("pixel_format", ctypes.c_uint32), # Pixel format 497 | ("type", ctypes.c_uint32), # Frame size type the device supports. 498 | ("_", _anonymous), # Frame size 499 | ("reserved", ctypes.c_uint32 * 2), 500 | ] 501 | 502 | _anonymous_ = ("_", ) 503 | 504 | index = None 505 | pixel_format = None 506 | type = None 507 | discrete = None 508 | stepwise = None 509 | reserved = None 510 | 511 | 512 | # Used in the VIDIOC_QUERYCTRL ioctl for querying controls 513 | class v4l2_queryctrl(ctypes.Structure): 514 | _fields_ = [ 515 | ("id", ctypes.c_uint32), 516 | ("type", ctypes.c_uint32), # v4l2_ctrl_type 517 | ("name", ctypes.c_char * 32), 518 | ("minimum", ctypes.c_int32), 519 | ("maximum", ctypes.c_int32), 520 | ("step", ctypes.c_int32), 521 | ("default_value", ctypes.c_int32), 522 | ("flags", ctypes.c_uint32), 523 | ("reserved", ctypes.c_uint32 * 2), 524 | ] 525 | 526 | id = None 527 | type = None 528 | name = None 529 | minimum = None 530 | maximum = None 531 | step = None 532 | default_value = None 533 | flags = None 534 | reserved = None 535 | 536 | 537 | # Used in the VIDIOC_QUERY_EXT_CTRL ioctl for querying extended controls 538 | class v4l2_query_ext_ctrl(ctypes.Structure): 539 | _fields_ = [ 540 | ("id", ctypes.c_uint32), 541 | ("type", ctypes.c_uint32), 542 | ("name", ctypes.c_char * 32), 543 | ("minimum", ctypes.c_int64), 544 | ("maximum", ctypes.c_int64), 545 | ("step", ctypes.c_uint64), 546 | ("default_value", ctypes.c_int64), 547 | ("flags", ctypes.c_uint32), 548 | ("elem_size", ctypes.c_uint32), 549 | ("elems", ctypes.c_uint32), 550 | ("nr_of_dims", ctypes.c_uint32), 551 | ("dims", ctypes.c_uint32 * V4L2_CTRL_MAX_DIMS), 552 | ("reserved", ctypes.c_uint32 * 32), 553 | ] 554 | 555 | id = None 556 | type = None 557 | name = None 558 | minimum = None 559 | maximum = None 560 | step = None 561 | default_value = None 562 | flags = None 563 | elem_size = None 564 | elems = None 565 | nr_of_dims = None 566 | dims = None 567 | reserved = None 568 | 569 | 570 | # Used in the VIDIOC_QUERYMENU ioctl for querying menu items 571 | class v4l2_querymenu(ctypes.Structure): 572 | 573 | class _anonymous(ctypes.Union): 574 | _fields_ = [ 575 | ("name", ctypes.c_char * 32), 576 | ("value", ctypes.c_int64), 577 | ] 578 | 579 | _fields_ = [ 580 | ("id", ctypes.c_uint32), 581 | ("index", ctypes.c_uint32), 582 | ("_", _anonymous), 583 | ("reserved", ctypes.c_uint32), 584 | ] 585 | 586 | _anonymous_ = ("_", ) 587 | _pack_ = 1 588 | 589 | id = None 590 | index = None 591 | name = None 592 | value = None 593 | reserved = None 594 | 595 | 596 | class v4l2_requestbuffers(ctypes.Structure): 597 | _fields_ = [ 598 | ("count", ctypes.c_uint32), 599 | ("type", ctypes.c_uint32), # v4l2_buf_type 600 | ("memory", ctypes.c_uint32), # v4l2_memory 601 | ("capabilities", ctypes.c_uint32), 602 | ("flags", ctypes.c_uint8), 603 | ("reserved", ctypes.c_uint8 * 3), 604 | ] 605 | 606 | count = None 607 | type = None 608 | memory = None 609 | capabilities = None 610 | flags = None 611 | reserved = None 612 | 613 | 614 | class v4l2_plane(ctypes.Structure): 615 | 616 | class _m(ctypes.Union): 617 | _fields_ = [ 618 | ("mem_offset", ctypes.c_uint32), 619 | ("userptr", ctypes.c_ulong), 620 | ("fd", ctypes.c_int32), 621 | ] 622 | 623 | _fields_ = [ 624 | ("bytesused", ctypes.c_uint32), 625 | ("length", ctypes.c_uint32), 626 | ("m", _m), 627 | ("data_offset", ctypes.c_uint32), 628 | ("reserved", ctypes.c_uint32 * 11), 629 | ] 630 | 631 | bytesused = None 632 | length = None 633 | m = _m() 634 | data_offset = None 635 | reserved = None 636 | 637 | 638 | class timeval(ctypes.Structure): 639 | _fields_ = [ 640 | ("tv_sec", ctypes.c_long), 641 | ("tv_usec", ctypes.c_long), 642 | ] 643 | 644 | tv_sec = None 645 | tv_usec = None 646 | 647 | 648 | class v4l2_timecode(ctypes.Structure): 649 | _fields_ = [ 650 | ("type", ctypes.c_uint32), 651 | ("flags", ctypes.c_uint32), 652 | ("frames", ctypes.c_uint8), 653 | ("seconds", ctypes.c_uint8), 654 | ("minutes", ctypes.c_uint8), 655 | ("hours", ctypes.c_uint8), 656 | ("userbits", ctypes.c_uint8 * 4), 657 | ] 658 | 659 | type = None 660 | flags = None 661 | frames = None 662 | seconds = None 663 | minutes = None 664 | hours = None 665 | userbits = None 666 | 667 | 668 | class v4l2_buffer(ctypes.Structure): 669 | 670 | class _m(ctypes.Union): 671 | _fields_ = [ 672 | ("offset", ctypes.c_uint32), 673 | ("userptr", ctypes.c_ulong), 674 | ("planes", ctypes.POINTER(v4l2_plane)), 675 | ("fd", ctypes.c_int32), 676 | ] 677 | 678 | offset = None 679 | userptr = None 680 | planes = None 681 | fd = None 682 | 683 | class _anonymous(ctypes.Union): 684 | _fields_ = [ 685 | ("request_fd", ctypes.c_int32), 686 | ("reserved", ctypes.c_uint32), 687 | ] 688 | 689 | _fields_ = [ 690 | ("index", ctypes.c_uint32), 691 | ("type", ctypes.c_uint32), 692 | ("bytesused", ctypes.c_uint32), 693 | ("flags", ctypes.c_uint32), 694 | ("field", ctypes.c_uint32), 695 | ("timestamp", timeval), 696 | ("timecode", v4l2_timecode), 697 | ("sequence", ctypes.c_uint32), 698 | ("memory", ctypes.c_uint32), 699 | ("m", _m), 700 | ("length", ctypes.c_uint32), 701 | ("reserved2", ctypes.c_uint32), 702 | ("_", _anonymous), 703 | ] 704 | 705 | _anonymous_ = ("_", ) 706 | 707 | index = None 708 | type = None 709 | bytesused = None 710 | flags = None 711 | field = None 712 | timestamp = timeval() 713 | timecode = v4l2_timecode() 714 | sequence = None 715 | memory = None 716 | m = _m() 717 | length = None 718 | reserved2 = None 719 | request_fd = None 720 | reserved = None 721 | 722 | 723 | class v4l2_fract(ctypes.Structure): 724 | _fields_ = [ 725 | ("numerator", ctypes.c_uint32), 726 | ("denominator", ctypes.c_uint32), 727 | ] 728 | 729 | numerator = None 730 | denominator = None 731 | 732 | 733 | class v4l2_captureparm(ctypes.Structure): 734 | _fields_ = [ 735 | ("capability", ctypes.c_uint32), # Supported modes 736 | ("capturemode", ctypes.c_uint32), # Current mode 737 | ("timeperframe", v4l2_fract), # Time per frame in seconds 738 | ("extendedmode", ctypes.c_uint32), # Driver-specific extensions 739 | ("readbuffers", ctypes.c_uint32), # # of buffers for read 740 | ("reserved", ctypes.c_uint32 * 4), 741 | ] 742 | 743 | capability = None 744 | capturemode = None 745 | timeperframe = v4l2_fract() 746 | extendedmode = None 747 | readbuffers = None 748 | reserved = None 749 | 750 | 751 | class v4l2_outputparm(ctypes.Structure): 752 | _fields_ = [ 753 | ("capability", ctypes.c_uint32), # Supported modes 754 | ("outputmode", ctypes.c_uint32), # Current mode 755 | ("timeperframe", v4l2_fract), # Time per frame in seconds 756 | ("extendedmode", ctypes.c_uint32), # Driver-specific extensions 757 | ("writebuffers", ctypes.c_uint32), # # of buffers for read 758 | ("reserved", ctypes.c_uint32 * 4), 759 | ] 760 | 761 | capability = None 762 | outputmode = None 763 | timeperframe = v4l2_fract() 764 | extendedmode = None 765 | writebuffers = None 766 | reserved = None 767 | 768 | 769 | class v4l2_streamparm(ctypes.Structure): 770 | 771 | class _parm(ctypes.Union): 772 | _fields_ = [ 773 | ("capture", v4l2_captureparm), 774 | ("output", v4l2_outputparm), 775 | ("raw_data", ctypes.c_uint8 * 200), 776 | ] 777 | 778 | capture = v4l2_captureparm() 779 | output = v4l2_outputparm() 780 | raw_data = None 781 | 782 | _fields_ = [ 783 | ("type", ctypes.c_uint32), # v4l2_buf_type 784 | ("parm", _parm) 785 | ] 786 | 787 | type = None 788 | parm = _parm() 789 | 790 | 791 | # Pixel format FOURCC 792 | 793 | 794 | # Four-character-code (FOURCC) 795 | def v4l2_fourcc(a, b, c, d): 796 | return ord(a) | (ord(b) << 8) | (ord(c) << 16) | (ord(d) << 24) 797 | 798 | 799 | def v4l2_fourcc_be(a, b, c, d): 800 | return v4l2_fourcc(a, b, c, d) | (1 << 31) 801 | 802 | 803 | # RGB formats (1 or 2 bytes per pixel) 804 | V4L2_PIX_FMT_RGB332 = v4l2_fourcc('R', 'G', 'B', '1') # 8 RGB-3-3-2 805 | V4L2_PIX_FMT_RGB444 = v4l2_fourcc('R', '4', '4', '4') # 16 xxxxrrrr ggggbbbb 806 | V4L2_PIX_FMT_ARGB444 = v4l2_fourcc('A', 'R', '1', '2') # 16 aaaarrrr ggggbbbb 807 | V4L2_PIX_FMT_XRGB444 = v4l2_fourcc('X', 'R', '1', '2') # 16 xxxxrrrr ggggbbbb 808 | V4L2_PIX_FMT_RGBA444 = v4l2_fourcc('R', 'A', '1', '2') # 16 rrrrgggg bbbbaaaa 809 | V4L2_PIX_FMT_RGBX444 = v4l2_fourcc('R', 'X', '1', '2') # 16 rrrrgggg bbbbxxxx 810 | V4L2_PIX_FMT_ABGR444 = v4l2_fourcc('A', 'B', '1', '2') # 16 aaaabbbb ggggrrrr 811 | V4L2_PIX_FMT_XBGR444 = v4l2_fourcc('X', 'B', '1', '2') # 16 xxxxbbbb ggggrrrr 812 | V4L2_PIX_FMT_BGRA444 = v4l2_fourcc('G', 'A', '1', '2') # 16 bbbbgggg rrrraaaa 813 | V4L2_PIX_FMT_BGRX444 = v4l2_fourcc('B', 'X', '1', '2') # 16 bbbbgggg rrrrxxxx 814 | V4L2_PIX_FMT_RGB555 = v4l2_fourcc('R', 'G', 'B', 'O') # 16 RGB-5-5-5 815 | V4L2_PIX_FMT_ARGB555 = v4l2_fourcc('A', 'R', '1', '5') # 16 ARGB-1-5-5-5 816 | V4L2_PIX_FMT_XRGB555 = v4l2_fourcc('X', 'R', '1', '5') # 16 XRGB-1-5-5-5 817 | V4L2_PIX_FMT_RGBA555 = v4l2_fourcc('R', 'A', '1', '5') # 16 RGBA-5-5-5-1 818 | V4L2_PIX_FMT_RGBX555 = v4l2_fourcc('R', 'X', '1', '5') # 16 RGBX-5-5-5-1 819 | V4L2_PIX_FMT_ABGR555 = v4l2_fourcc('A', 'B', '1', '5') # 16 ABGR-1-5-5-5 820 | V4L2_PIX_FMT_XBGR555 = v4l2_fourcc('X', 'B', '1', '5') # 16 XBGR-1-5-5-5 821 | V4L2_PIX_FMT_BGRA555 = v4l2_fourcc('B', 'A', '1', '5') # 16 BGRA-5-5-5-1 822 | V4L2_PIX_FMT_BGRX555 = v4l2_fourcc('B', 'X', '1', '5') # 16 BGRX-5-5-5-1 823 | V4L2_PIX_FMT_RGB565 = v4l2_fourcc('R', 'G', 'B', 'P') # 16 RGB-5-6-5 824 | V4L2_PIX_FMT_RGB555X = v4l2_fourcc('R', 'G', 'B', 'Q') # 16 RGB-5-5-5 BE 825 | V4L2_PIX_FMT_ARGB555X = v4l2_fourcc_be('A', 'R', '1', '5') # 16 ARGB-5-5-5 BE 826 | V4L2_PIX_FMT_XRGB555X = v4l2_fourcc_be('X', 'R', '1', '5') # 16 XRGB-5-5-5 BE 827 | V4L2_PIX_FMT_RGB565X = v4l2_fourcc('R', 'G', 'B', 'R') # 16 RGB-5-6-5 BE 828 | 829 | # RGB formats (3 or 4 bytes per pixel) 830 | V4L2_PIX_FMT_BGR666 = v4l2_fourcc('B', 'G', 'R', 'H') # 18 BGR-6-6-6 831 | V4L2_PIX_FMT_BGR24 = v4l2_fourcc('B', 'G', 'R', '3') # 24 BGR-8-8-8 832 | V4L2_PIX_FMT_RGB24 = v4l2_fourcc('R', 'G', 'B', '3') # 24 RGB-8-8-8 833 | V4L2_PIX_FMT_BGR32 = v4l2_fourcc('B', 'G', 'R', '4') # 32 BGR-8-8-8-8 834 | V4L2_PIX_FMT_ABGR32 = v4l2_fourcc('A', 'R', '2', '4') # 32 BGRA-8-8-8-8 835 | V4L2_PIX_FMT_XBGR32 = v4l2_fourcc('X', 'R', '2', '4') # 32 BGRX-8-8-8-8 836 | V4L2_PIX_FMT_BGRA32 = v4l2_fourcc('R', 'A', '2', '4') # 32 ABGR-8-8-8-8 837 | V4L2_PIX_FMT_BGRX32 = v4l2_fourcc('R', 'X', '2', '4') # 32 XBGR-8-8-8-8 838 | V4L2_PIX_FMT_RGB32 = v4l2_fourcc('R', 'G', 'B', '4') # 32 RGB-8-8-8-8 839 | V4L2_PIX_FMT_RGBA32 = v4l2_fourcc('A', 'B', '2', '4') # 32 RGBA-8-8-8-8 840 | V4L2_PIX_FMT_RGBX32 = v4l2_fourcc('X', 'B', '2', '4') # 32 RGBX-8-8-8-8 841 | V4L2_PIX_FMT_ARGB32 = v4l2_fourcc('B', 'A', '2', '4') # 32 ARGB-8-8-8-8 842 | V4L2_PIX_FMT_XRGB32 = v4l2_fourcc('B', 'X', '2', '4') # 32 XRGB-8-8-8-8 843 | 844 | # Grey formats 845 | V4L2_PIX_FMT_GREY = v4l2_fourcc('G', 'R', 'E', 'Y') # 8 Greyscale 846 | V4L2_PIX_FMT_Y4 = v4l2_fourcc('Y', '0', '4', ' ') # 4 Greyscale 847 | V4L2_PIX_FMT_Y6 = v4l2_fourcc('Y', '0', '6', ' ') # 6 Greyscale 848 | V4L2_PIX_FMT_Y10 = v4l2_fourcc('Y', '1', '0', ' ') # 10 Greyscale 849 | V4L2_PIX_FMT_Y12 = v4l2_fourcc('Y', '1', '2', ' ') # 12 Greyscale 850 | V4L2_PIX_FMT_Y14 = v4l2_fourcc('Y', '1', '4', ' ') # 14 Greyscale 851 | V4L2_PIX_FMT_Y16 = v4l2_fourcc('Y', '1', '6', ' ') # 16 Greyscale 852 | V4L2_PIX_FMT_Y16_BE = v4l2_fourcc_be('Y', '1', '6', ' ') # 16 Greyscale BE 853 | 854 | # Grey bit-packed formats 855 | V4L2_PIX_FMT_Y10BPACK = v4l2_fourcc('Y', '1', '0', 856 | 'B') # 10 Greyscale bit-packed 857 | V4L2_PIX_FMT_Y10P = v4l2_fourcc('Y', '1', '0', 858 | 'P') # 10 Greyscale, MIPI RAW10 packed 859 | 860 | # Palette formats 861 | V4L2_PIX_FMT_PAL8 = v4l2_fourcc('P', 'A', 'L', '8') # 8 8-bit palette 862 | 863 | # Chrominance formats 864 | V4L2_PIX_FMT_UV8 = v4l2_fourcc('U', 'V', '8', ' ') # 8 UV 4:4 865 | 866 | # Luminance+Chrominance formats 867 | V4L2_PIX_FMT_YUYV = v4l2_fourcc('Y', 'U', 'Y', 'V') # 16 YUV 4:2:2 868 | V4L2_PIX_FMT_YYUV = v4l2_fourcc('Y', 'Y', 'U', 'V') # 16 YUV 4:2:2 869 | V4L2_PIX_FMT_YVYU = v4l2_fourcc('Y', 'V', 'Y', 'U') # 16 YVU 4:2:2 870 | V4L2_PIX_FMT_UYVY = v4l2_fourcc('U', 'Y', 'V', 'Y') # 16 YUV 4:2:2 871 | V4L2_PIX_FMT_VYUY = v4l2_fourcc('V', 'Y', 'U', 'Y') # 16 YUV 4:2:2 872 | V4L2_PIX_FMT_Y41P = v4l2_fourcc('Y', '4', '1', 'P') # 12 YUV 4:1:1 873 | V4L2_PIX_FMT_YUV444 = v4l2_fourcc('Y', '4', '4', '4') # 16 xxxxyyyy uuuuvvvv 874 | V4L2_PIX_FMT_YUV555 = v4l2_fourcc('Y', 'U', 'V', 'O') # 16 YUV-5-5-5 875 | V4L2_PIX_FMT_YUV565 = v4l2_fourcc('Y', 'U', 'V', 'P') # 16 YUV-5-6-5 876 | V4L2_PIX_FMT_YUV24 = v4l2_fourcc('Y', 'U', 'V', '3') # 24 YUV-8-8-8 877 | V4L2_PIX_FMT_YUV32 = v4l2_fourcc('Y', 'U', 'V', '4') # 32 YUV-8-8-8-8 878 | V4L2_PIX_FMT_AYUV32 = v4l2_fourcc('A', 'Y', 'U', 'V') # 32 AYUV-8-8-8-8 879 | V4L2_PIX_FMT_XYUV32 = v4l2_fourcc('X', 'Y', 'U', 'V') # 32 XYUV-8-8-8-8 880 | V4L2_PIX_FMT_VUYA32 = v4l2_fourcc('V', 'U', 'Y', 'A') # 32 VUYA-8-8-8-8 881 | V4L2_PIX_FMT_VUYX32 = v4l2_fourcc('V', 'U', 'Y', 'X') # 32 VUYX-8-8-8-8 882 | V4L2_PIX_FMT_M420 = v4l2_fourcc( 883 | 'M', '4', '2', '0') # 12 YUV 4:2:0 2 lines y, 1 line uv interleaved 884 | 885 | # two planes -- one Y, one Cr + Cb interleaved 886 | V4L2_PIX_FMT_NV12 = v4l2_fourcc('N', 'V', '1', '2') # 12 Y/CbCr 4:2:0 887 | V4L2_PIX_FMT_NV21 = v4l2_fourcc('N', 'V', '2', '1') # 12 Y/CrCb 4:2:0 888 | V4L2_PIX_FMT_NV16 = v4l2_fourcc('N', 'V', '1', '6') # 16 Y/CbCr 4:2:2 889 | V4L2_PIX_FMT_NV61 = v4l2_fourcc('N', 'V', '6', '1') # 16 Y/CrCb 4:2:2 890 | V4L2_PIX_FMT_NV24 = v4l2_fourcc('N', 'V', '2', '4') # 24 Y/CbCr 4:4:4 891 | V4L2_PIX_FMT_NV42 = v4l2_fourcc('N', 'V', '4', '2') # 24 Y/CrCb 4:4:4 892 | 893 | # two non contiguous planes - one Y, one Cr + Cb interleaved 894 | V4L2_PIX_FMT_NV12M = v4l2_fourcc('N', 'M', '1', '2') # 12 Y/CbCr 4:2:0 895 | V4L2_PIX_FMT_NV21M = v4l2_fourcc('N', 'M', '2', '1') # 21 Y/CrCb 4:2:0 896 | V4L2_PIX_FMT_NV16M = v4l2_fourcc('N', 'M', '1', '6') # 16 Y/CbCr 4:2:2 897 | V4L2_PIX_FMT_NV61M = v4l2_fourcc('N', 'M', '6', '1') # 16 Y/CrCb 4:2:2 898 | 899 | # three planes - Y Cb, Cr 900 | V4L2_PIX_FMT_YUV410 = v4l2_fourcc('Y', 'U', 'V', '9') # 9 YUV 4:1:0 901 | V4L2_PIX_FMT_YVU410 = v4l2_fourcc('Y', 'V', 'U', '9') # 9 YVU 4:1:0 902 | V4L2_PIX_FMT_YUV411P = v4l2_fourcc('4', '1', '1', 'P') # 12 YVU411 planar 903 | V4L2_PIX_FMT_YUV420 = v4l2_fourcc('Y', 'U', '1', '2') # 12 YUV 4:2:0 904 | V4L2_PIX_FMT_YVU420 = v4l2_fourcc('Y', 'V', '1', '2') # 12 YVU 4:2:0 905 | V4L2_PIX_FMT_YUV422P = v4l2_fourcc('4', '2', '2', 'P') # 16 YVU422 planar 906 | 907 | # three non contiguous planes - Y, Cb, Cr 908 | V4L2_PIX_FMT_YUV420M = v4l2_fourcc('Y', 'M', '1', '2') # 12 YUV420 planar 909 | V4L2_PIX_FMT_YVU420M = v4l2_fourcc('Y', 'M', '2', '1') # 12 YVU420 planar 910 | V4L2_PIX_FMT_YUV422M = v4l2_fourcc('Y', 'M', '1', '6') # 16 YUV422 planar 911 | V4L2_PIX_FMT_YVU422M = v4l2_fourcc('Y', 'M', '6', '1') # 16 YVU422 planar 912 | V4L2_PIX_FMT_YUV444M = v4l2_fourcc('Y', 'M', '2', '4') # 24 YUV444 planar 913 | V4L2_PIX_FMT_YVU444M = v4l2_fourcc('Y', 'M', '4', '2') # 24 YVU444 planar 914 | 915 | # Tiled YUV formats 916 | V4L2_PIX_FMT_NV12_4L4 = v4l2_fourcc('V', 'T', '1', 917 | '2') # 12 Y/CbCr 4:2:0 4x4 tiles 918 | V4L2_PIX_FMT_NV12_16L16 = v4l2_fourcc('H', 'M', '1', 919 | '2') # 12 Y/CbCr 4:2:0 16x16 tiles 920 | V4L2_PIX_FMT_NV12_32L32 = v4l2_fourcc('S', 'T', '1', 921 | '2') # 12 Y/CbCr 4:2:0 32x32 tiles 922 | 923 | # Tiled YUV formats, non contiguous planes 924 | V4L2_PIX_FMT_NV12MT = v4l2_fourcc('T', 'M', '1', 925 | '2') # 12 Y/CbCr 4:2:0 64x32 tiles 926 | V4L2_PIX_FMT_NV12MT_16X16 = v4l2_fourcc('V', 'M', '1', 927 | '2') # 12 Y/CbCr 4:2:0 16x16 tiles 928 | V4L2_PIX_FMT_NV12M_8L128 = v4l2_fourcc('N', 'A', '1', 929 | '2') # Y/CbCr 4:2:0 8x128 tiles 930 | V4L2_PIX_FMT_NV12M_10BE_8L128 = v4l2_fourcc_be( 931 | 'N', 'T', '1', '2') # Y/CbCr 4:2:0 10-bit 8x128 tiles 932 | 933 | # Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm 934 | V4L2_PIX_FMT_SBGGR8 = v4l2_fourcc('B', 'A', '8', '1') # 8 BGBG.. GRGR.. 935 | V4L2_PIX_FMT_SGBRG8 = v4l2_fourcc('G', 'B', 'R', 'G') # 8 GBGB.. RGRG.. 936 | V4L2_PIX_FMT_SGRBG8 = v4l2_fourcc('G', 'R', 'B', 'G') # 8 GRGR.. BGBG.. 937 | V4L2_PIX_FMT_SRGGB8 = v4l2_fourcc('R', 'G', 'G', 'B') # 8 RGRG.. GBGB.. 938 | V4L2_PIX_FMT_SBGGR10 = v4l2_fourcc('B', 'G', '1', '0') # 10 BGBG.. GRGR.. 939 | V4L2_PIX_FMT_SGBRG10 = v4l2_fourcc('G', 'B', '1', '0') # 10 GBGB.. RGRG.. 940 | V4L2_PIX_FMT_SGRBG10 = v4l2_fourcc('B', 'A', '1', '0') # 10 GRGR.. BGBG.. 941 | V4L2_PIX_FMT_SRGGB10 = v4l2_fourcc('R', 'G', '1', '0') # 10 RGRG.. GBGB.. 942 | # 10bit raw bayer packed, 5 bytes for every 4 pixels 943 | V4L2_PIX_FMT_SBGGR10P = v4l2_fourcc('p', 'B', 'A', 'A') 944 | V4L2_PIX_FMT_SGBRG10P = v4l2_fourcc('p', 'G', 'A', 'A') 945 | V4L2_PIX_FMT_SGRBG10P = v4l2_fourcc('p', 'g', 'A', 'A') 946 | V4L2_PIX_FMT_SRGGB10P = v4l2_fourcc('p', 'R', 'A', 'A') 947 | # 10bit raw bayer a-law compressed to 8 bits 948 | V4L2_PIX_FMT_SBGGR10ALAW8 = v4l2_fourcc('a', 'B', 'A', '8') 949 | V4L2_PIX_FMT_SGBRG10ALAW8 = v4l2_fourcc('a', 'G', 'A', '8') 950 | V4L2_PIX_FMT_SGRBG10ALAW8 = v4l2_fourcc('a', 'g', 'A', '8') 951 | V4L2_PIX_FMT_SRGGB10ALAW8 = v4l2_fourcc('a', 'R', 'A', '8') 952 | # 10bit raw bayer DPCM compressed to 8 bits 953 | V4L2_PIX_FMT_SBGGR10DPCM8 = v4l2_fourcc('b', 'B', 'A', '8') 954 | V4L2_PIX_FMT_SGBRG10DPCM8 = v4l2_fourcc('b', 'G', 'A', '8') 955 | V4L2_PIX_FMT_SGRBG10DPCM8 = v4l2_fourcc('B', 'D', '1', '0') 956 | V4L2_PIX_FMT_SRGGB10DPCM8 = v4l2_fourcc('b', 'R', 'A', '8') 957 | V4L2_PIX_FMT_SBGGR12 = v4l2_fourcc('B', 'G', '1', '2') # 12 BGBG.. GRGR.. 958 | V4L2_PIX_FMT_SGBRG12 = v4l2_fourcc('G', 'B', '1', '2') # 12 GBGB.. RGRG.. 959 | V4L2_PIX_FMT_SGRBG12 = v4l2_fourcc('B', 'A', '1', '2') # 12 GRGR.. BGBG.. 960 | V4L2_PIX_FMT_SRGGB12 = v4l2_fourcc('R', 'G', '1', '2') # 12 RGRG.. GBGB.. 961 | # 12bit raw bayer packed, 6 bytes for every 4 pixels 962 | V4L2_PIX_FMT_SBGGR12P = v4l2_fourcc('p', 'B', 'C', 'C') 963 | V4L2_PIX_FMT_SGBRG12P = v4l2_fourcc('p', 'G', 'C', 'C') 964 | V4L2_PIX_FMT_SGRBG12P = v4l2_fourcc('p', 'g', 'C', 'C') 965 | V4L2_PIX_FMT_SRGGB12P = v4l2_fourcc('p', 'R', 'C', 'C') 966 | V4L2_PIX_FMT_SBGGR14 = v4l2_fourcc('B', 'G', '1', '4') # 14 BGBG.. GRGR.. 967 | V4L2_PIX_FMT_SGBRG14 = v4l2_fourcc('G', 'B', '1', '4') # 14 GBGB.. RGRG.. 968 | V4L2_PIX_FMT_SGRBG14 = v4l2_fourcc('G', 'R', '1', '4') # 14 GRGR.. BGBG.. 969 | V4L2_PIX_FMT_SRGGB14 = v4l2_fourcc('R', 'G', '1', '4') # 14 RGRG.. GBGB.. 970 | # 14bit raw bayer packed, 7 bytes for every 4 pixels 971 | V4L2_PIX_FMT_SBGGR14P = v4l2_fourcc('p', 'B', 'E', 'E') 972 | V4L2_PIX_FMT_SGBRG14P = v4l2_fourcc('p', 'G', 'E', 'E') 973 | V4L2_PIX_FMT_SGRBG14P = v4l2_fourcc('p', 'g', 'E', 'E') 974 | V4L2_PIX_FMT_SRGGB14P = v4l2_fourcc('p', 'R', 'E', 'E') 975 | V4L2_PIX_FMT_SBGGR16 = v4l2_fourcc('B', 'Y', 'R', '2') # 16 BGBG.. GRGR.. 976 | V4L2_PIX_FMT_SGBRG16 = v4l2_fourcc('G', 'B', '1', '6') # 16 GBGB.. RGRG.. 977 | V4L2_PIX_FMT_SGRBG16 = v4l2_fourcc('G', 'R', '1', '6') # 16 GRGR.. BGBG.. 978 | V4L2_PIX_FMT_SRGGB16 = v4l2_fourcc('R', 'G', '1', '6') # 16 RGRG.. GBGB.. 979 | 980 | # HSV formats 981 | V4L2_PIX_FMT_HSV24 = v4l2_fourcc('H', 'S', 'V', '3') 982 | V4L2_PIX_FMT_HSV32 = v4l2_fourcc('H', 'S', 'V', '4') 983 | 984 | # compressed formats 985 | V4L2_PIX_FMT_MJPEG = v4l2_fourcc('M', 'J', 'P', 'G') # Motion-JPEG 986 | V4L2_PIX_FMT_JPEG = v4l2_fourcc('J', 'P', 'E', 'G') # JFIF JPEG 987 | V4L2_PIX_FMT_DV = v4l2_fourcc('d', 'v', 's', 'd') # 1394 988 | V4L2_PIX_FMT_MPEG = v4l2_fourcc('M', 'P', 'E', 'G') # MPEG-1/2/4 Multiplexed 989 | V4L2_PIX_FMT_H264 = v4l2_fourcc('H', '2', '6', '4') # H264 with start codes 990 | V4L2_PIX_FMT_H264_NO_SC = v4l2_fourcc('A', 'V', 'C', 991 | '1') # H264 without start codes 992 | V4L2_PIX_FMT_H264_MVC = v4l2_fourcc('M', '2', '6', '4') # H264 MVC 993 | V4L2_PIX_FMT_H263 = v4l2_fourcc('H', '2', '6', '3') # H263 994 | V4L2_PIX_FMT_MPEG1 = v4l2_fourcc('M', 'P', 'G', '1') # MPEG-1 ES 995 | V4L2_PIX_FMT_MPEG2 = v4l2_fourcc('M', 'P', 'G', '2') # MPEG-2 ES 996 | V4L2_PIX_FMT_MPEG2_SLICE = v4l2_fourcc('M', 'G', '2', 997 | 'S') # MPEG-2 parsed slice data 998 | V4L2_PIX_FMT_MPEG4 = v4l2_fourcc('M', 'P', 'G', '4') # MPEG-4 part 2 ES 999 | V4L2_PIX_FMT_XVID = v4l2_fourcc('X', 'V', 'I', 'D') # Xvid 1000 | V4L2_PIX_FMT_VC1_ANNEX_G = v4l2_fourcc( 1001 | 'V', 'C', '1', 'G') # SMPTE 421M Annex G compliant stream 1002 | V4L2_PIX_FMT_VC1_ANNEX_L = v4l2_fourcc( 1003 | 'V', 'C', '1', 'L') # SMPTE 421M Annex L compliant stream 1004 | V4L2_PIX_FMT_VP8 = v4l2_fourcc('V', 'P', '8', '0') # VP8 1005 | V4L2_PIX_FMT_VP8_FRAME = v4l2_fourcc('V', 'P', '8', 'F') # VP8 parsed frame 1006 | V4L2_PIX_FMT_VP9 = v4l2_fourcc('V', 'P', '9', '0') # VP9 1007 | V4L2_PIX_FMT_VP9_FRAME = v4l2_fourcc('V', 'P', '9', 'F') # VP9 parsed frame 1008 | V4L2_PIX_FMT_HEVC = v4l2_fourcc('H', 'E', 'V', 'C') # HEVC aka H.265 1009 | V4L2_PIX_FMT_FWHT = v4l2_fourcc('F', 'W', 'H', 1010 | 'T') # Fast Walsh Hadamard Transform (vicodec) 1011 | V4L2_PIX_FMT_FWHT_STATELESS = v4l2_fourcc('S', 'F', 'W', 1012 | 'H') # Stateless FWHT (vicodec) 1013 | V4L2_PIX_FMT_H264_SLICE = v4l2_fourcc('S', '2', '6', '4') # H264 parsed slices 1014 | 1015 | # Vendor-specific formats 1016 | V4L2_PIX_FMT_CPIA1 = v4l2_fourcc('C', 'P', 'I', 'A') # cpia1 YUV 1017 | V4L2_PIX_FMT_WNVA = v4l2_fourcc('W', 'N', 'V', 'A') # Winnov hw compress 1018 | V4L2_PIX_FMT_SN9C10X = v4l2_fourcc('S', '9', '1', '0') # SN9C10x compression 1019 | V4L2_PIX_FMT_SN9C20X_I420 = v4l2_fourcc('S', '9', '2', 1020 | '0') # SN9C20x YUV 4:2:0 1021 | V4L2_PIX_FMT_PWC1 = v4l2_fourcc('P', 'W', 'C', '1') # pwc older webcam 1022 | V4L2_PIX_FMT_PWC2 = v4l2_fourcc('P', 'W', 'C', '2') # pwc newer webcam 1023 | V4L2_PIX_FMT_ET61X251 = v4l2_fourcc('E', '6', '2', '5') # ET61X251 compression 1024 | V4L2_PIX_FMT_SPCA501 = v4l2_fourcc('S', '5', '0', '1') # YUYV per line 1025 | V4L2_PIX_FMT_SPCA505 = v4l2_fourcc('S', '5', '0', '5') # YYUV per line 1026 | V4L2_PIX_FMT_SPCA508 = v4l2_fourcc('S', '5', '0', '8') # YUVY per line 1027 | V4L2_PIX_FMT_SPCA561 = v4l2_fourcc('S', '5', '6', '1') # compressed GBRG bayer 1028 | V4L2_PIX_FMT_PAC207 = v4l2_fourcc('P', '2', '0', '7') # compressed BGGR bayer 1029 | V4L2_PIX_FMT_MR97310A = v4l2_fourcc('M', '3', '1', 1030 | '0') # compressed BGGR bayer 1031 | V4L2_PIX_FMT_JL2005BCD = v4l2_fourcc('J', 'L', '2', 1032 | '0') # compressed RGGB bayer 1033 | V4L2_PIX_FMT_SN9C2028 = v4l2_fourcc('S', 'O', 'N', 1034 | 'X') # compressed GBRG bayer 1035 | V4L2_PIX_FMT_SQ905C = v4l2_fourcc('9', '0', '5', 'C') # compressed RGGB bayer 1036 | V4L2_PIX_FMT_PJPG = v4l2_fourcc('P', 'J', 'P', 'G') # Pixart 73xx JPEG 1037 | V4L2_PIX_FMT_OV511 = v4l2_fourcc('O', '5', '1', '1') # ov511 JPEG 1038 | V4L2_PIX_FMT_OV518 = v4l2_fourcc('O', '5', '1', '8') # ov518 JPEG 1039 | V4L2_PIX_FMT_STV0680 = v4l2_fourcc('S', '6', '8', '0') # stv0680 bayer 1040 | V4L2_PIX_FMT_TM6000 = v4l2_fourcc('T', 'M', '6', '0') # tm5600/tm60x0 1041 | V4L2_PIX_FMT_CIT_YYVYUY = v4l2_fourcc('C', 'I', 'T', 1042 | 'V') # one line of Y then 1 line of VYUY 1043 | V4L2_PIX_FMT_KONICA420 = v4l2_fourcc( 1044 | 'K', 'O', 'N', 'I') # YUV420 planar in blocks of 256 pixels 1045 | V4L2_PIX_FMT_JPGL = v4l2_fourcc('J', 'P', 'G', 'L') # JPEG-Lite 1046 | V4L2_PIX_FMT_SE401 = v4l2_fourcc('S', '4', '0', 1047 | '1') # se401 janggu compressed rgb 1048 | V4L2_PIX_FMT_S5C_UYVY_JPG = v4l2_fourcc('S', '5', 'C', 1049 | 'I') # S5C73M3 interleaved UYVY/JPEG 1050 | V4L2_PIX_FMT_Y8I = v4l2_fourcc('Y', '8', 'I', 1051 | ' ') # Greyscale 8-bit L/R interleaved 1052 | V4L2_PIX_FMT_Y12I = v4l2_fourcc('Y', '1', '2', 1053 | 'I') # Greyscale 12-bit L/R interleaved 1054 | V4L2_PIX_FMT_Z16 = v4l2_fourcc('Z', '1', '6', ' ') # Depth data 16-bit 1055 | V4L2_PIX_FMT_MT21C = v4l2_fourcc('M', 'T', '2', 1056 | '1') # Mediatek compressed block mode 1057 | V4L2_PIX_FMT_MM21 = v4l2_fourcc( 1058 | 'M', 'M', '2', '1') # Mediatek 8-bit block mode, two non-contiguous planes 1059 | V4L2_PIX_FMT_INZI = v4l2_fourcc( 1060 | 'I', 'N', 'Z', 'I') # Intel Planar Greyscale 10-bit and Depth 16-bit 1061 | V4L2_PIX_FMT_CNF4 = v4l2_fourcc( 1062 | 'C', 'N', 'F', '4') # Intel 4-bit packed depth confidence information 1063 | V4L2_PIX_FMT_HI240 = v4l2_fourcc('H', 'I', '2', '4') # BTTV 8-bit dithered RGB 1064 | 1065 | # 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused 1066 | V4L2_PIX_FMT_IPU3_SBGGR10 = v4l2_fourcc('i', 'p', '3', 1067 | 'b') # IPU3 packed 10-bit BGGR bayer 1068 | V4L2_PIX_FMT_IPU3_SGBRG10 = v4l2_fourcc('i', 'p', '3', 1069 | 'g') # IPU3 packed 10-bit GBRG bayer 1070 | V4L2_PIX_FMT_IPU3_SGRBG10 = v4l2_fourcc('i', 'p', '3', 1071 | 'G') # IPU3 packed 10-bit GRBG bayer 1072 | V4L2_PIX_FMT_IPU3_SRGGB10 = v4l2_fourcc('i', 'p', '3', 1073 | 'r') # IPU3 packed 10-bit RGGB bayer 1074 | 1075 | 1076 | class v4l2_frmival_stepwise(ctypes.Structure): 1077 | _fields_ = [ 1078 | ("min", v4l2_fract), 1079 | ("max", v4l2_fract), 1080 | ("step", v4l2_fract), 1081 | ] 1082 | 1083 | min = v4l2_fract() 1084 | max = v4l2_fract() 1085 | step = v4l2_fract() 1086 | 1087 | 1088 | class v4l2_frmivalenum(ctypes.Structure): 1089 | 1090 | class _anonymous(ctypes.Union): 1091 | _fields_ = [ 1092 | ("discrete", v4l2_fract), 1093 | ("stepwise", v4l2_frmival_stepwise), 1094 | ] 1095 | 1096 | _fields_ = [ 1097 | ("index", ctypes.c_uint32), 1098 | ("pixel_format", ctypes.c_uint32), 1099 | ("width", ctypes.c_uint32), 1100 | ("height", ctypes.c_uint32), 1101 | ("type", ctypes.c_uint32), 1102 | ("_", _anonymous), 1103 | ("reserved", ctypes.c_uint32 * 2), 1104 | ] 1105 | 1106 | _anonymous_ = ("_", ) 1107 | 1108 | index = None 1109 | pixel_format = None 1110 | width = None 1111 | height = None 1112 | type = None 1113 | discrete = v4l2_fract() 1114 | stepwise = v4l2_frmival_stepwise() 1115 | reserved = None 1116 | 1117 | 1118 | # 1119 | # ioctl codes 1120 | # 1121 | _IOC_NRBITS = 8 1122 | _IOC_TYPEBITS = 8 1123 | _IOC_SIZEBITS = 14 1124 | 1125 | _IOC_NRSHIFT = 0 1126 | _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS 1127 | _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS 1128 | _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS 1129 | 1130 | _IOC_WRITE = 1 1131 | _IOC_READ = 2 1132 | 1133 | 1134 | def _IOC(dir_, type_, nr, size): 1135 | return (dir_ << _IOC_DIRSHIFT) | (ord(type_) << _IOC_TYPESHIFT) | ( 1136 | nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT) 1137 | 1138 | 1139 | def _IOC_TYPECHECK(t): 1140 | return ctypes.sizeof(t) 1141 | 1142 | 1143 | def _IOR(type_, nr, size): 1144 | return _IOC(_IOC_READ, type_, nr, _IOC_TYPECHECK(size)) 1145 | 1146 | 1147 | def _IOW(type_, nr, size): 1148 | return _IOC(_IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) 1149 | 1150 | 1151 | def _IOWR(type_, nr, size): 1152 | return _IOC(_IOC_READ | _IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) 1153 | 1154 | 1155 | VIDIOC_QUERYCAP = _IOR('V', 0, v4l2_capability) 1156 | VIDIOC_ENUM_FMT = _IOWR('V', 2, v4l2_fmtdesc) 1157 | VIDIOC_G_FMT = _IOWR('V', 4, v4l2_format) 1158 | VIDIOC_S_FMT = _IOWR('V', 5, v4l2_format) 1159 | VIDIOC_REQBUFS = _IOWR('V', 8, v4l2_requestbuffers) 1160 | VIDIOC_QUERYBUF = _IOWR('V', 9, v4l2_buffer) 1161 | VIDIOC_QBUF = _IOWR('V', 15, v4l2_buffer) 1162 | VIDIOC_DQBUF = _IOWR('V', 17, v4l2_buffer) 1163 | VIDIOC_STREAMON = _IOW('V', 18, ctypes.c_int) 1164 | VIDIOC_STREAMOFF = _IOW('V', 19, ctypes.c_int) 1165 | VIDIOC_G_PARM = _IOWR('V', 21, v4l2_streamparm) 1166 | VIDIOC_S_PARM = _IOWR('V', 22, v4l2_streamparm) 1167 | VIDIOC_G_CTRL = _IOWR('V', 27, v4l2_control) 1168 | VIDIOC_S_CTRL = _IOWR('V', 28, v4l2_control) 1169 | VIDIOC_QUERYCTRL = _IOWR('V', 36, v4l2_queryctrl) 1170 | VIDIOC_QUERYMENU = _IOWR('V', 37, v4l2_querymenu) 1171 | VIDIOC_G_EXT_CTRLS = _IOWR('V', 71, v4l2_ext_controls) 1172 | VIDIOC_S_EXT_CTRLS = _IOWR('V', 72, v4l2_ext_controls) 1173 | VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, v4l2_frmsizeenum) 1174 | VIDIOC_ENUM_FRAMEINTERVALS = _IOWR('V', 75, v4l2_frmivalenum) 1175 | VIDIOC_QUERY_EXT_CTRL = _IOWR('V', 103, v4l2_query_ext_ctrl) 1176 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='pyrav4l2', 5 | version='1.0', 6 | author='Antmicro Ltd', 7 | description="Pythonic, Really Awesome V4L2 utility", 8 | author_email='contact@antmicro.com', 9 | packages=find_packages(include=["pyrav4l2"]), 10 | include_package_data=True, 11 | license='Apache Software License (http://www.apache.org/licenses/LICENSE-2.0)', 12 | classifiers=[ 13 | "Programming Language :: Python :: 3", 14 | "License :: OSI Approved :: Apache Software License", 15 | "Operating System :: OS Independent", 16 | ], 17 | ) 18 | --------------------------------------------------------------------------------