Human friendly video for linux
Project description
v4l2py
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.7.
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
andasyncio
) without the usual tricks like usingasyncio.to_thread
Installation
From within your favorite python environment:
$ pip install v4l2py
Usage
Without further ado:
>>> 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:
>>> from v4l2py.device import Device, BufferType
>>> cam = Device.from_id(0)
>>> cam.open()
>>> cam.info.card
'Integrated_Webcam_HD: Integrate'
>>> cam.info.capabilities
<Capability.STREAMING|EXT_PIX_FORMAT|VIDEO_CAPTURE: 69206017>
>>> cam.info.formats
[ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'Motion-JPEG',
flags=<ImageFormatFlag.COMPRESSED: 1>, pixelformat=<PixelFormat.MJPEG: 1196444237>),
ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'YUYV 4:2:2',
flags=<ImageFormatFlag.0: 0>, pixelformat=<PixelFormat.YUYV: 1448695129>)]
>>> cam.get_format(BufferType.VIDEO_CAPTURE)
Format(width=640, height=480, pixelformat=<PixelFormat.MJPEG: 1196444237>}
>>> for ctrl in cam.controls.values(): print(ctrl)
<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>
<IntegerControl contrast min=0 max=255 step=1 default=32 value=32>
<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>
<IntegerControl hue min=-180 max=180 step=1 default=0 value=0>
<BooleanControl white_balance_automatic default=True value=True>
<IntegerControl gamma min=90 max=150 step=1 default=120 value=120>
<MenuControl power_line_frequency default=1 value=1>
<IntegerControl white_balance_temperature min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive>
<IntegerControl sharpness min=0 max=7 step=1 default=2 value=2>
<IntegerControl backlight_compensation min=0 max=2 step=1 default=1 value=1>
<MenuControl auto_exposure default=3 value=3>
<IntegerControl exposure_time_absolute min=4 max=1250 step=1 default=156 value=156 flags=inactive>
<BooleanControl exposure_dynamic_framerate default=False value=False>
>>> cam.controls["saturation"]
<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>
>>> cam.controls["saturation"].id
9963778
>>> cam.controls[9963778]
<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>
>>> cam.controls.brightness
<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>
>>> cam.controls.brightness.value = 64
>>> cam.controls.brightness
<IntegerControl brightness min=0 max=255 step=1 default=128 value=64>
(see also v4l2py-ctl example)
asyncio
v4l2py is asyncio friendly:
$ 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 and web async 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 and web gevent 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:
$ pip install flask
# 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 '<html><img src="/stream" /></html>'
@app.route("/stream")
def stream():
return flask.Response(
gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
run with:
$ FLASK_APP=web flask run -h 0.0.0.0
Point your browser to 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:
with Device.from_id(0) as cam:
for frame in cam:
buff = io.BytesIO(frame)
Now:
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:
>>> 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}")
<Control brightness type=integer min=0 max=255 step=1 default=128 value=255>
<Control contrast type=integer min=0 max=255 step=1 default=32 value=255>
<Control saturation type=integer min=0 max=100 step=1 default=64 value=100>
<Control hue type=integer min=-180 max=180 step=1 default=0 value=0>
<Control white_balance_automatic type=boolean min=0 max=1 step=1 default=1 value=1>
<Control gamma type=integer min=90 max=150 step=1 default=120 value=150>
<Control gain type=integer min=1 max=7 step=1 default=1 value=1>
<Control power_line_frequency type=menu min=0 max=2 step=1 default=2 value=2>
- 0: Disabled
- 1: 50 Hz
- 2: 60 Hz
<Control white_balance_temperature type=integer min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive>
<Control sharpness type=integer min=0 max=7 step=1 default=2 value=7>
<Control backlight_compensation type=integer min=0 max=1 step=1 default=0 value=1>
<Control auto_exposure type=menu min=0 max=3 step=1 default=3 value=3>
- 1: Manual Mode
- 3: Aperture Priority Mode
<Control exposure_time_absolute type=integer min=10 max=333 step=1 default=156 value=156 flags=inactive>
<Control exposure_dynamic_framerate type=boolean min=0 max=1 step=1 default=0 value=1>
>>> type(cam.controls.exposure_dynamic_framerate.value)
<class 'int'>
Now:
>>> 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}")
<IntegerControl brightness min=0 max=255 step=1 default=128 value=255>
<IntegerControl contrast min=0 max=255 step=1 default=32 value=255>
<IntegerControl saturation min=0 max=100 step=1 default=64 value=100>
<IntegerControl hue min=-180 max=180 step=1 default=0 value=0>
<BooleanControl white_balance_automatic default=True value=True>
<IntegerControl gamma min=90 max=150 step=1 default=120 value=150>
<IntegerControl gain min=1 max=7 step=1 default=1 value=1>
<MenuControl power_line_frequency default=2 value=2>
- 0: Disabled
- 1: 50 Hz
- 2: 60 Hz
<IntegerControl white_balance_temperature min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive>
<IntegerControl sharpness min=0 max=7 step=1 default=2 value=7>
<IntegerControl backlight_compensation min=0 max=1 step=1 default=0 value=1>
<MenuControl auto_exposure default=3 value=3>
- 1: Manual Mode
- 3: Aperture Priority Mode
<IntegerControl exposure_time_absolute min=10 max=333 step=1 default=156 value=156 flags=inactive>
<BooleanControl exposure_dynamic_framerate default=False value=True>
>>> type(cam.controls.white_balance_automatic.value)
<class 'bool'>
>>> cam.controls.white_balance_automatic.value
<BooleanControl white_balance_automatic default=True value=True>
>>> cam.controls.white_balance_automatic.value = False
<BooleanControl white_balance_automatic default=True value=False>
>>> wba = cam.controls.white_balance_automatic
>>> wba.value = "enable" # or "on", "1", "true", "yes"
>>> wba
<BooleanControl white_balance_automatic default=True value=True>
>>> wba.value = "off" # or "disable", "0", "false", "no"
>>> wba
<BooleanControl white_balance_automatic default=True value=False>
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 <https://www.kernel.org/doc/html/v6.2/userspace-api/media/v4l/v4l2.html>
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file v4l2py-2.3.0.tar.gz
.
File metadata
- Download URL: v4l2py-2.3.0.tar.gz
- Upload date:
- Size: 46.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 2d4dd81c1963186b25ac7e989cbf2ff013f881047ce2142710df04beea9a3525 |
|
MD5 | df7031817d4ebfedd94b1068abde4af2 |
|
BLAKE2b-256 | fc8571223071be9c61ac08ea18cf1211410d115b0d8c08d666fa2c42d04d2202 |