Package: v4l2py
v4l2pyVideo for Linux 2 (V4L2) python libraryA 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 usecv2.VideoCapture?Here is a list of features that this library provides which I couldn't find in other libraries:List available V4L2 devicesObtain 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 VideoOutputIntegration with non blocking coroutine based applications (geventandasyncio)
without the usual tricks like usingasyncio.to_threadInstallationFrom within your favorite python environment:$pipinstallv4l2pyUsageWithout further ado:>>>fromv4l2pyimportDevice>>>withDevice.from_id(0)ascam:>>>fori,frameinenumerate(cam):...print(f"frame #{i}:{len(frame)}bytes")...ifi>9:...break...frame#0: 54630 bytesframe#1: 50184 bytesframe#2: 44054 bytesframe#3: 42822 bytesframe#4: 42116 bytesframe#5: 41868 bytesframe#6: 41322 bytesframe#7: 40896 bytesframe#8: 40844 bytesframe#9: 40714 bytesframe#10: 40662 bytesGetting information about the device:>>>fromv4l2py.deviceimportDevice,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>}>>>forctrlincam.controls.values():print(ctrl)<IntegerControlbrightnessmin=0max=255step=1default=128value=128><IntegerControlcontrastmin=0max=255step=1default=32value=32><IntegerControlsaturationmin=0max=100step=1default=64value=64><IntegerControlhuemin=-180max=180step=1default=0value=0><BooleanControlwhite_balance_automaticdefault=Truevalue=True><IntegerControlgammamin=90max=150step=1default=120value=120><MenuControlpower_line_frequencydefault=1value=1><IntegerControlwhite_balance_temperaturemin=2800max=6500step=1default=4000value=4000flags=inactive><IntegerControlsharpnessmin=0max=7step=1default=2value=2><IntegerControlbacklight_compensationmin=0max=2step=1default=1value=1><MenuControlauto_exposuredefault=3value=3><IntegerControlexposure_time_absolutemin=4max=1250step=1default=156value=156flags=inactive><BooleanControlexposure_dynamic_frameratedefault=Falsevalue=False>>>>cam.controls["saturation"]<IntegerControlsaturationmin=0max=100step=1default=64value=64>>>>cam.controls["saturation"].id9963778>>>cam.controls[9963778]<IntegerControlsaturationmin=0max=100step=1default=64value=64>>>>cam.controls.brightness<IntegerControlbrightnessmin=0max=255step=1default=128value=128>>>>cam.controls.brightness.value=64>>>cam.controls.brightness<IntegerControlbrightnessmin=0max=255step=1default=128value=64>(see alsov4l2py-ctlexample)asynciov4l2py is asyncio friendly:$python-masyncio

>>>fromv4l2pyimportDevice
>>>withDevice.from_id(0)ascamera:
...asyncforframeincamera:
...print(f"frame {len(frame)}")frame10224frame10304frame10224frame10136...(checkbasic asyncandweb asyncexamples)geventv4l2py 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
...(checkbasic geventandweb geventexamples)Bonus trackYou'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:$pipinstallflask# web.pyimportflaskfromv4l2pyimportDeviceapp=flask.Flask('basic-web-cam')defgen_frames():withDevice.from_id(0)ascam:forframeincam:yieldb"--frame\r\nContent-Type: image/jpeg\r\n\r\n"+frame.data+b"\r\n"@app.route("/")defindex():return'<html><img src="/stream" /></html>'@app.route("/stream")defstream():returnflask.Response(gen_frames(),mimetype='multipart/x-mixed-replace; boundary=frame')run with:$FLASK_APP=webflaskrun-h0.0.0.0Point your browser to127.0.0.1:5000and you should see
your camera rolling!Migrating from 1.x to 2A 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 withbytesor access theframe.dataitem:Before:withDevice.from_id(0)ascam:forframeincam:buff=io.BytesIO(frame)Now:withDevice.from_id(0)ascam:forframeincam:frame=bytes(frame)# or frame = frame.databuff=io.BytesIO(frame)Improved device controlsDevice 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:>>>fromv4l2pyimportDevice>>>cam=Device.from_id(0)>>>cam.open()>>>forctrlincam.controls.values():...print(ctrl)...foriteminctrl.menu.values():...print(f" -{item.index}:{item.name}")<Controlbrightnesstype=integermin=0max=255step=1default=128value=255><Controlcontrasttype=integermin=0max=255step=1default=32value=255><Controlsaturationtype=integermin=0max=100step=1default=64value=100><Controlhuetype=integermin=-180max=180step=1default=0value=0><Controlwhite_balance_automatictype=booleanmin=0max=1step=1default=1value=1><Controlgammatype=integermin=90max=150step=1default=120value=150><Controlgaintype=integermin=1max=7step=1default=1value=1><Controlpower_line_frequencytype=menumin=0max=2step=1default=2value=2>-0:Disabled-1:50Hz-2:60Hz<Controlwhite_balance_temperaturetype=integermin=2800max=6500step=1default=4000value=4000flags=inactive><Controlsharpnesstype=integermin=0max=7step=1default=2value=7><Controlbacklight_compensationtype=integermin=0max=1step=1default=0value=1><Controlauto_exposuretype=menumin=0max=3step=1default=3value=3>-1:ManualMode-3:AperturePriorityMode<Controlexposure_time_absolutetype=integermin=10max=333step=1default=156value=156flags=inactive><Controlexposure_dynamic_frameratetype=booleanmin=0max=1step=1default=0value=1>>>>type(cam.controls.exposure_dynamic_framerate.value)<class'int'>Now:>>>fromv4l2py.deviceimportDevice,MenuControl>>>cam=Device.from_id(0)>>>cam.open()>>>forctrlincam.controls.values():...print(ctrl)...ifisinstance(ctrl,MenuControl):...for(index,name)inctrl.items():...print(f" -{index}:{name}")<IntegerControlbrightnessmin=0max=255step=1default=128value=255><IntegerControlcontrastmin=0max=255step=1default=32value=255><IntegerControlsaturationmin=0max=100step=1default=64value=100><IntegerControlhuemin=-180max=180step=1default=0value=0><BooleanControlwhite_balance_automaticdefault=Truevalue=True><IntegerControlgammamin=90max=150step=1default=120value=150><IntegerControlgainmin=1max=7step=1default=1value=1><MenuControlpower_line_frequencydefault=2value=2>-0:Disabled-1:50Hz-2:60Hz<IntegerControlwhite_balance_temperaturemin=2800max=6500step=1default=4000value=4000flags=inactive><IntegerControlsharpnessmin=0max=7step=1default=2value=7><IntegerControlbacklight_compensationmin=0max=1step=1default=0value=1><MenuControlauto_exposuredefault=3value=3>-1:ManualMode-3:AperturePriorityMode<IntegerControlexposure_time_absolutemin=10max=333step=1default=156value=156flags=inactive><BooleanControlexposure_dynamic_frameratedefault=Falsevalue=True>>>>type(cam.controls.white_balance_automatic.value)<class'bool'>>>>cam.controls.white_balance_automatic.value<BooleanControlwhite_balance_automaticdefault=Truevalue=True>>>>cam.controls.white_balance_automatic.value=False<BooleanControlwhite_balance_automaticdefault=Truevalue=False>>>>wba=cam.controls.white_balance_automatic>>>wba.value="enable"# or "on", "1", "true", "yes">>>wba<BooleanControlwhite_balance_automaticdefault=Truevalue=True>>>>wba.value="off"# or "disable", "0", "false", "no">>>wba<BooleanControlwhite_balance_automaticdefault=Truevalue=False>The initial upgrade path for existing code is to request the legacy interface
by passinglegacy_controls=Truewhen instantiating theDeviceobject, useLegacyControlinstead ofControlfor instantiations, andBaseControlfor isinstance() checks. And in the unlikely case your code does isinstance()
checks forMenuItem, these should be changed toLegacyMenuItem.ReferencesSee thelinux/videodev2.hheader file for details.Video for Linux Two Specification <https://www.kernel.org/doc/html/v6.2/userspace-api/media/v4l/v4l2.html>