An open API service indexing awesome lists of open source software.

https://github.com/tiagocoutinho/v4l2py

V4L2 python library
https://github.com/tiagocoutinho/v4l2py

python v4l2 video4linux

Last synced: 12 months ago
JSON representation

V4L2 python library

Awesome Lists containing this project

README

          

# v4l2py

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

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

[![V4L2py][pypi-version]](https://pypi.python.org/pypi/v4l2py)
[![Python Versions][pypi-python-versions]](https://pypi.python.org/pypi/v4l2py)
![License][license]
[![CI][CI]](https://github.com/tiagocoutinho/v4l2py/actions/workflows/ci.yml)

Video for Linux 2 (V4L2) python library

A two purpose API:

* high level Device API for humans to play with :-)
* raw python binding for the v4l2 (video4linux2) userspace API, using ctypes (don't even
bother wasting your time here. You probably won't use it)

Only works on python >= 3.9.

## Why?

So, why another library dedicated to video control? Couldn't I just use `cv2.VideoCapture`?

Here is a list of features that this library provides which I couldn't find in other libraries:

* List available V4L2 devices
* Obtain detailed information about a device (name, driver, capabilities, available formats)
* Fine control over the camera parameters (ex: resolution, format, brightness, contrast, etc)
* Fine control resource management to take profit of memory map, DMA or user pointers (buffers)
* Detailed information about a frame (timestamp, frame number, etc)
* Write to VideoOutput
* Integration with non blocking coroutine based applications (`gevent` and `asyncio`)
without the usual tricks like using `asyncio.to_thread`

## Installation

From within your favorite python environment:

```bash
$ pip install v4l2py
```

## Usage

Without further ado:

```python
>>> from v4l2py import Device
>>> with Device.from_id(0) as cam:
>>> for i, frame in enumerate(cam):
... print(f"frame #{i}: {len(frame)} bytes")
... if i > 9:
... break
...
frame #0: 54630 bytes
frame #1: 50184 bytes
frame #2: 44054 bytes
frame #3: 42822 bytes
frame #4: 42116 bytes
frame #5: 41868 bytes
frame #6: 41322 bytes
frame #7: 40896 bytes
frame #8: 40844 bytes
frame #9: 40714 bytes
frame #10: 40662 bytes
```

Getting information about the device:

```python
>>> from v4l2py.device import Device, BufferType

>>> cam = Device.from_id(0)
>>> cam.open()
>>> cam.info.card
'Integrated_Webcam_HD: Integrate'

>>> cam.info.capabilities

>>> cam.info.formats
[ImageFormat(type=, description=b'Motion-JPEG',
flags=, pixelformat=),
ImageFormat(type=, description=b'YUYV 4:2:2',
flags=, pixelformat=)]

>>> cam.get_format(BufferType.VIDEO_CAPTURE)
Format(width=640, height=480, pixelformat=}

>>> for ctrl in cam.controls.values(): print(ctrl)

>>> cam.controls["saturation"]

>>> cam.controls["saturation"].id
9963778
>>> cam.controls[9963778]

>>> cam.controls.brightness

>>> cam.controls.brightness.value = 64
>>> cam.controls.brightness

```

(see also [v4l2py-ctl](examples/v4l2py-ctl.py) example)

### asyncio

v4l2py is asyncio friendly:

```bash
$ python -m asyncio

>>> from v4l2py import Device
>>> with Device.from_id(0) as camera:
... async for frame in camera:
... print(f"frame {len(frame)}")
frame 10224
frame 10304
frame 10224
frame 10136
...
```

(check [basic async](examples/basic_async.py) and [web async](examples/web/async.py) examples)

### gevent

v4l2py is also gevent friendly:

```
$ python

>>> from v4l2py import Device, GeventIO
>>> with Device.from_id(0, io=GeventIO) as camera:
... for frame in camera:
... print(f"frame {len(frame)}")
frame 10224
frame 10304
frame 10224
frame 10136
...
```

(check [basic gevent](examples/basic_gevent.py) and [web gevent](examples/web/sync.py) examples)

## Bonus track

You've been patient enough to read until here so, just for you,
a 20 line gem: a flask web server displaying your device on the web:

```bash
$ pip install flask
```

```python
# web.py

import flask
from v4l2py import Device

app = flask.Flask('basic-web-cam')

def gen_frames():
with Device.from_id(0) as cam:
for frame in cam:
yield b"--frame\r\nContent-Type: image/jpeg\r\n\r\n" + frame.data + b"\r\n"

@app.route("/")
def index():
return ''

@app.route("/stream")
def stream():
return flask.Response(
gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
```

run with:

```bash
$ FLASK_APP=web flask run -h 0.0.0.0
```

Point your browser to [127.0.0.1:5000](http://127.0.0.1:5000) and you should see
your camera rolling!

## Migrating from 1.x to 2

A frame changed from a simple bytes object to a Frame which contains
the data plus all frame metadata.

As a consequence, when migrating from 1.x to 2, you will need to cast
frame object with `bytes` or access the `frame.data` item:

Before:

```python
with Device.from_id(0) as cam:
for frame in cam:
buff = io.BytesIO(frame)
```

Now:

```python
with Device.from_id(0) as cam:
for frame in cam:
frame = bytes(frame) # or frame = frame.data
buff = io.BytesIO(frame)
```

## Improved device controls

Device controls have been improved to provide a more pythonic interface. The
new interface is the default now; however, the legacy interface can be
requested: `Device.from_id(x, legacy_controls=True)`.

Before:
```python
>>> from v4l2py import Device
>>> cam = Device.from_id(0)
>>> cam.open()
>>> for ctrl in cam.controls.values():
... print(ctrl)
... for item in ctrl.menu.values():
... print(f" - {item.index}: {item.name}")

- 0: Disabled
- 1: 50 Hz
- 2: 60 Hz

- 1: Manual Mode
- 3: Aperture Priority Mode

>>> type(cam.controls.exposure_dynamic_framerate.value)

```

Now:
```python
>>> from v4l2py.device import Device, MenuControl
>>> cam = Device.from_id(0)
>>> cam.open()
>>> for ctrl in cam.controls.values():
... print(ctrl)
... if isinstance(ctrl, MenuControl):
... for (index, name) in ctrl.items():
... print(f" - {index}: {name}")

- 0: Disabled
- 1: 50 Hz
- 2: 60 Hz

- 1: Manual Mode
- 3: Aperture Priority Mode

>>> type(cam.controls.white_balance_automatic.value)

>>> cam.controls.white_balance_automatic.value

>>> cam.controls.white_balance_automatic.value = False

>>> wba = cam.controls.white_balance_automatic
>>> wba.value = "enable" # or "on", "1", "true", "yes"
>>> wba

>>> wba.value = "off" # or "disable", "0", "false", "no"
>>> wba

```

The initial upgrade path for existing code is to request the legacy interface
by passing `legacy_controls=True` when instantiating the `Device` object, use
`LegacyControl` instead of `Control` for instantiations, and `BaseControl`
for isinstance() checks. And in the unlikely case your code does isinstance()
checks for `MenuItem`, these should be changed to `LegacyMenuItem`.

## References

See the ``linux/videodev2.h`` header file for details.

* `Video for Linux Two Specification `

[pypi-python-versions]: https://img.shields.io/pypi/pyversions/v4l2py.svg
[pypi-version]: https://img.shields.io/pypi/v/v4l2py.svg
[pypi-status]: https://img.shields.io/pypi/status/v4l2py.svg
[license]: https://img.shields.io/pypi/l/v4l2py.svg
[CI]: https://github.com/tiagocoutinho/v4l2py/actions/workflows/ci.yml/badge.svg