{"id":16190635,"url":"https://github.com/tiagocoutinho/v4l2py","last_synced_at":"2025-07-25T17:42:12.894Z","repository":{"id":40425481,"uuid":"361213419","full_name":"tiagocoutinho/v4l2py","owner":"tiagocoutinho","description":"V4L2 python library","archived":false,"fork":false,"pushed_at":"2024-03-11T20:54:20.000Z","size":343,"stargazers_count":71,"open_issues_count":4,"forks_count":20,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T19:19:19.657Z","etag":null,"topics":["python","v4l2","video4linux"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"aspotton/python3-v4l2","license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tiagocoutinho.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-24T16:37:45.000Z","updated_at":"2025-01-04T17:17:15.000Z","dependencies_parsed_at":"2024-06-19T00:22:17.000Z","dependency_job_id":"94d6975d-ca2d-42c9-a0c0-cec37a3c3aa3","html_url":"https://github.com/tiagocoutinho/v4l2py","commit_stats":{"total_commits":195,"total_committers":11,"mean_commits":"17.727272727272727","dds":"0.36923076923076925","last_synced_commit":"1e0101f3abe191f09930fe2d712fa0c6dc0af52b"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagocoutinho%2Fv4l2py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagocoutinho%2Fv4l2py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagocoutinho%2Fv4l2py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagocoutinho%2Fv4l2py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tiagocoutinho","download_url":"https://codeload.github.com/tiagocoutinho/v4l2py/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248094991,"owners_count":21046770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","v4l2","video4linux"],"created_at":"2024-10-10T07:43:48.856Z","updated_at":"2025-04-09T19:19:26.740Z","avatar_url":"https://github.com/tiagocoutinho.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# v4l2py\n\n**Since version 3.0 this project is now a shim for `linuxpy.video`**\n\n**Please consider using [linuxpy](https://github.com/tiagocoutinho/linuxpy) directly.**\n\n[![V4L2py][pypi-version]](https://pypi.python.org/pypi/v4l2py)\n[![Python Versions][pypi-python-versions]](https://pypi.python.org/pypi/v4l2py)\n![License][license]\n[![CI][CI]](https://github.com/tiagocoutinho/v4l2py/actions/workflows/ci.yml)\n\nVideo for Linux 2 (V4L2) python library\n\nA two purpose API:\n\n* high level Device API for humans to play with :-)\n* raw python binding for the v4l2 (video4linux2) userspace API, using ctypes (don't even\n  bother wasting your time here. You probably won't use it)\n\nOnly works on python \u003e= 3.9.\n\n## Why?\n\nSo, why another library dedicated to video control? Couldn't I just use `cv2.VideoCapture`?\n\nHere is a list of features that this library provides which I couldn't find in other libraries:\n\n* List available V4L2 devices\n* Obtain detailed information about a device (name, driver, capabilities, available formats)\n* Fine control over the camera parameters (ex: resolution, format, brightness, contrast, etc)\n* Fine control resource management to take profit of memory map, DMA or user pointers (buffers)\n* Detailed information about a frame (timestamp, frame number, etc)\n* Write to VideoOutput\n* Integration with non blocking coroutine based applications (`gevent` and `asyncio`) \n  without the usual tricks like using `asyncio.to_thread`\n\n\n## Installation\n\nFrom within your favorite python environment:\n\n```bash\n$ pip install v4l2py\n```\n\n## Usage\n\nWithout further ado:\n\n```python\n\u003e\u003e\u003e from v4l2py import Device\n\u003e\u003e\u003e with Device.from_id(0) as cam:\n\u003e\u003e\u003e     for i, frame in enumerate(cam):\n...         print(f\"frame #{i}: {len(frame)} bytes\")\n...         if i \u003e 9:\n...             break\n...\nframe #0: 54630 bytes\nframe #1: 50184 bytes\nframe #2: 44054 bytes\nframe #3: 42822 bytes\nframe #4: 42116 bytes\nframe #5: 41868 bytes\nframe #6: 41322 bytes\nframe #7: 40896 bytes\nframe #8: 40844 bytes\nframe #9: 40714 bytes\nframe #10: 40662 bytes\n```\n\nGetting information about the device:\n\n```python\n\u003e\u003e\u003e from v4l2py.device import Device, BufferType\n\n\u003e\u003e\u003e cam = Device.from_id(0)\n\u003e\u003e\u003e cam.open()\n\u003e\u003e\u003e cam.info.card\n'Integrated_Webcam_HD: Integrate'\n\n\u003e\u003e\u003e cam.info.capabilities\n\u003cCapability.STREAMING|EXT_PIX_FORMAT|VIDEO_CAPTURE: 69206017\u003e\n\n\u003e\u003e\u003e cam.info.formats\n[ImageFormat(type=\u003cBufferType.VIDEO_CAPTURE: 1\u003e, description=b'Motion-JPEG',\n             flags=\u003cImageFormatFlag.COMPRESSED: 1\u003e, pixelformat=\u003cPixelFormat.MJPEG: 1196444237\u003e),\n ImageFormat(type=\u003cBufferType.VIDEO_CAPTURE: 1\u003e, description=b'YUYV 4:2:2',\n             flags=\u003cImageFormatFlag.0: 0\u003e, pixelformat=\u003cPixelFormat.YUYV: 1448695129\u003e)]\n\n\u003e\u003e\u003e cam.get_format(BufferType.VIDEO_CAPTURE)\nFormat(width=640, height=480, pixelformat=\u003cPixelFormat.MJPEG: 1196444237\u003e}\n\n\u003e\u003e\u003e for ctrl in cam.controls.values(): print(ctrl)\n\u003cIntegerControl brightness min=0 max=255 step=1 default=128 value=128\u003e\n\u003cIntegerControl contrast min=0 max=255 step=1 default=32 value=32\u003e\n\u003cIntegerControl saturation min=0 max=100 step=1 default=64 value=64\u003e\n\u003cIntegerControl hue min=-180 max=180 step=1 default=0 value=0\u003e\n\u003cBooleanControl white_balance_automatic default=True value=True\u003e\n\u003cIntegerControl gamma min=90 max=150 step=1 default=120 value=120\u003e\n\u003cMenuControl power_line_frequency default=1 value=1\u003e\n\u003cIntegerControl white_balance_temperature min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive\u003e\n\u003cIntegerControl sharpness min=0 max=7 step=1 default=2 value=2\u003e\n\u003cIntegerControl backlight_compensation min=0 max=2 step=1 default=1 value=1\u003e\n\u003cMenuControl auto_exposure default=3 value=3\u003e\n\u003cIntegerControl exposure_time_absolute min=4 max=1250 step=1 default=156 value=156 flags=inactive\u003e\n\u003cBooleanControl exposure_dynamic_framerate default=False value=False\u003e\n\n\u003e\u003e\u003e cam.controls[\"saturation\"]\n\u003cIntegerControl saturation min=0 max=100 step=1 default=64 value=64\u003e\n\n\u003e\u003e\u003e cam.controls[\"saturation\"].id\n9963778\n\u003e\u003e\u003e cam.controls[9963778]\n\u003cIntegerControl saturation min=0 max=100 step=1 default=64 value=64\u003e\n\n\u003e\u003e\u003e cam.controls.brightness\n\u003cIntegerControl brightness min=0 max=255 step=1 default=128 value=128\u003e\n\u003e\u003e\u003e cam.controls.brightness.value = 64\n\u003e\u003e\u003e cam.controls.brightness\n\u003cIntegerControl brightness min=0 max=255 step=1 default=128 value=64\u003e\n```\n\n(see also [v4l2py-ctl](examples/v4l2py-ctl.py) example)\n\n### asyncio\n\nv4l2py is asyncio friendly:\n\n```bash\n$ python -m asyncio\n\n\u003e\u003e\u003e from v4l2py import Device\n\u003e\u003e\u003e with Device.from_id(0) as camera:\n...     async for frame in camera:\n...         print(f\"frame {len(frame)}\")\nframe 10224\nframe 10304\nframe 10224\nframe 10136\n...\n```\n\n(check [basic async](examples/basic_async.py) and [web async](examples/web/async.py) examples)\n\n### gevent\n\nv4l2py is also gevent friendly:\n\n```\n$ python\n\n\u003e\u003e\u003e from v4l2py import Device, GeventIO\n\u003e\u003e\u003e with Device.from_id(0, io=GeventIO) as camera:\n...     for frame in camera:\n...         print(f\"frame {len(frame)}\")\nframe 10224\nframe 10304\nframe 10224\nframe 10136\n...\n```\n\n(check [basic gevent](examples/basic_gevent.py) and [web gevent](examples/web/sync.py) examples)\n\n## Bonus track\n\nYou've been patient enough to read until here so, just for you,\na 20 line gem: a flask web server displaying your device on the web:\n\n```bash\n$ pip install flask\n```\n\n```python\n# web.py\n\nimport flask\nfrom v4l2py import Device\n\napp = flask.Flask('basic-web-cam')\n\ndef gen_frames():\n    with Device.from_id(0) as cam:\n        for frame in cam:\n            yield b\"--frame\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n\" + frame.data + b\"\\r\\n\"\n\n@app.route(\"/\")\ndef index():\n    return '\u003chtml\u003e\u003cimg src=\"/stream\" /\u003e\u003c/html\u003e'\n\n@app.route(\"/stream\")\ndef stream():\n    return flask.Response(\n        gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')\n```\n\nrun with:\n\n```bash\n$ FLASK_APP=web flask run -h 0.0.0.0\n```\n\nPoint your browser to [127.0.0.1:5000](http://127.0.0.1:5000) and you should see\nyour camera rolling!\n\n## Migrating from 1.x to 2\n\nA frame changed from a simple bytes object to a Frame which contains\nthe data plus all frame metadata.\n\nAs a consequence, when migrating from 1.x to 2, you will need to cast\nframe object with `bytes` or access the `frame.data` item:\n\nBefore:\n\n```python\nwith Device.from_id(0) as cam:\n    for frame in cam:\n        buff = io.BytesIO(frame)\n```\n\nNow:\n\n```python\nwith Device.from_id(0) as cam:\n    for frame in cam:\n        frame = bytes(frame)  # or frame = frame.data\n        buff = io.BytesIO(frame)\n```\n\n## Improved device controls\n\nDevice controls have been improved to provide a more pythonic interface. The\nnew interface is the default now; however, the legacy interface can be\nrequested: `Device.from_id(x, legacy_controls=True)`.\n\nBefore:\n```python\n\u003e\u003e\u003e from v4l2py import Device\n\u003e\u003e\u003e cam = Device.from_id(0)\n\u003e\u003e\u003e cam.open()\n\u003e\u003e\u003e for ctrl in cam.controls.values():\n...     print(ctrl)\n...     for item in ctrl.menu.values():\n...             print(f\" - {item.index}: {item.name}\")\n\u003cControl brightness type=integer min=0 max=255 step=1 default=128 value=255\u003e\n\u003cControl contrast type=integer min=0 max=255 step=1 default=32 value=255\u003e\n\u003cControl saturation type=integer min=0 max=100 step=1 default=64 value=100\u003e\n\u003cControl hue type=integer min=-180 max=180 step=1 default=0 value=0\u003e\n\u003cControl white_balance_automatic type=boolean min=0 max=1 step=1 default=1 value=1\u003e\n\u003cControl gamma type=integer min=90 max=150 step=1 default=120 value=150\u003e\n\u003cControl gain type=integer min=1 max=7 step=1 default=1 value=1\u003e\n\u003cControl power_line_frequency type=menu min=0 max=2 step=1 default=2 value=2\u003e\n - 0: Disabled\n - 1: 50 Hz\n - 2: 60 Hz\n\u003cControl white_balance_temperature type=integer min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive\u003e\n\u003cControl sharpness type=integer min=0 max=7 step=1 default=2 value=7\u003e\n\u003cControl backlight_compensation type=integer min=0 max=1 step=1 default=0 value=1\u003e\n\u003cControl auto_exposure type=menu min=0 max=3 step=1 default=3 value=3\u003e\n - 1: Manual Mode\n - 3: Aperture Priority Mode\n\u003cControl exposure_time_absolute type=integer min=10 max=333 step=1 default=156 value=156 flags=inactive\u003e\n\u003cControl exposure_dynamic_framerate type=boolean min=0 max=1 step=1 default=0 value=1\u003e\n\n\u003e\u003e\u003e type(cam.controls.exposure_dynamic_framerate.value)\n\u003cclass 'int'\u003e\n```\n\nNow:\n```python\n\u003e\u003e\u003e from v4l2py.device import Device, MenuControl\n\u003e\u003e\u003e cam = Device.from_id(0)\n\u003e\u003e\u003e cam.open()\n\u003e\u003e\u003e for ctrl in cam.controls.values():\n...     print(ctrl)\n...     if isinstance(ctrl, MenuControl):\n...             for (index, name) in ctrl.items():\n...                     print(f\" - {index}: {name}\")\n\u003cIntegerControl brightness min=0 max=255 step=1 default=128 value=255\u003e\n\u003cIntegerControl contrast min=0 max=255 step=1 default=32 value=255\u003e\n\u003cIntegerControl saturation min=0 max=100 step=1 default=64 value=100\u003e\n\u003cIntegerControl hue min=-180 max=180 step=1 default=0 value=0\u003e\n\u003cBooleanControl white_balance_automatic default=True value=True\u003e\n\u003cIntegerControl gamma min=90 max=150 step=1 default=120 value=150\u003e\n\u003cIntegerControl gain min=1 max=7 step=1 default=1 value=1\u003e\n\u003cMenuControl power_line_frequency default=2 value=2\u003e\n - 0: Disabled\n - 1: 50 Hz\n - 2: 60 Hz\n\u003cIntegerControl white_balance_temperature min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive\u003e\n\u003cIntegerControl sharpness min=0 max=7 step=1 default=2 value=7\u003e\n\u003cIntegerControl backlight_compensation min=0 max=1 step=1 default=0 value=1\u003e\n\u003cMenuControl auto_exposure default=3 value=3\u003e\n - 1: Manual Mode\n - 3: Aperture Priority Mode\n\u003cIntegerControl exposure_time_absolute min=10 max=333 step=1 default=156 value=156 flags=inactive\u003e\n\u003cBooleanControl exposure_dynamic_framerate default=False value=True\u003e\n\n\u003e\u003e\u003e type(cam.controls.white_balance_automatic.value)\n\u003cclass 'bool'\u003e\n\u003e\u003e\u003e cam.controls.white_balance_automatic.value\n\u003cBooleanControl white_balance_automatic default=True value=True\u003e\n\u003e\u003e\u003e cam.controls.white_balance_automatic.value = False\n\u003cBooleanControl white_balance_automatic default=True value=False\u003e\n\n\u003e\u003e\u003e wba = cam.controls.white_balance_automatic\n\u003e\u003e\u003e wba.value = \"enable\"    # or \"on\", \"1\", \"true\", \"yes\"\n\u003e\u003e\u003e wba\n\u003cBooleanControl white_balance_automatic default=True value=True\u003e\n\u003e\u003e\u003e wba.value = \"off\"       # or \"disable\", \"0\", \"false\", \"no\"\n\u003e\u003e\u003e wba\n\u003cBooleanControl white_balance_automatic default=True value=False\u003e\n```\n\nThe initial upgrade path for existing code is to request the legacy interface\nby passing `legacy_controls=True` when instantiating the `Device` object, use\n`LegacyControl` instead of `Control` for instantiations, and `BaseControl`\nfor isinstance() checks. And in the unlikely case your code does isinstance()\nchecks for `MenuItem`, these should be changed to `LegacyMenuItem`.\n\n## References\n\nSee the ``linux/videodev2.h`` header file for details.\n\n\n* `Video for Linux Two Specification \u003chttps://www.kernel.org/doc/html/v6.2/userspace-api/media/v4l/v4l2.html\u003e`\n\n[pypi-python-versions]: https://img.shields.io/pypi/pyversions/v4l2py.svg\n[pypi-version]: https://img.shields.io/pypi/v/v4l2py.svg\n[pypi-status]: https://img.shields.io/pypi/status/v4l2py.svg\n[license]: https://img.shields.io/pypi/l/v4l2py.svg\n[CI]: https://github.com/tiagocoutinho/v4l2py/actions/workflows/ci.yml/badge.svg\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiagocoutinho%2Fv4l2py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftiagocoutinho%2Fv4l2py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiagocoutinho%2Fv4l2py/lists"}