Package: linuxpy
linuxpyHuman friendly interface to linux subsystems using python.Provides python access to several linux subsystems like V4L2, input and MIDI.There is experimental, undocumented, incomplete and unstable access to USB.Requirements:python >= 3.9Fairly recent linux kernelInstalled kernel modules you want to accessAnd yes, it is true: there are no python libraries required! Also there are no
C libraries required. Everything is done here through direct ioctl, read and
write calls. Ain't linux wonderful?InstallationFrom within your favorite python environment:$pipinstalllinuxpyTo run the examples you'll need:$pipinstalllinuxpy[examples]To develop, run tests, build package, lint, etc you'll need:$pipinstalllinuxpy[dev]SubsystemsVideoVideo for Linux 2 (V4L2) python libraryWithout further ado:>>>fromlinuxpy.video.deviceimportDevice>>>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:>>>fromlinuxpy.video.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>...<BooleanControlexposure_dynamic_frameratedefault=Falsevalue=False>>>>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)asynciolinuxpy.video is asyncio friendly:$python-masyncio>>> from linuxpy.video.device import Device>>> with Device.from_id(0) as camera:...     async for frame in camera:...         print(f"frame {len(frame)}")frame 10224frame 10304frame 10224frame 10136...(checkbasic asyncandweb asyncexamples)geventlinuxpy.video is also gevent friendly:$ python

>>> from linuxpy.io import GeventIO
>>> from linuxpy.video.device import Device
>>> 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)Video outputIt is possible to write to a video output capable device (ex: v4l2loopback).
The following example shows how to grab frames from device 0 and write them
to device 10>>> from linuxpy.video.device import Device, VideoOutput, BufferType>>> dev_source = Device.from_id(0)>>> dev_sink = Device.from_id(10)>>> with dev_source, dev_target:>>>     source = VideoCapture(dev_source)>>>     sink = VideoOutput(dev_sink)>>>     source.set_format(640, 480, "MJPG")>>>     sink.set_format(640, 480, "MJPG")>>>     with source, sink:>>>         for frame in source:>>>             sink.write(frame.data)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.pyimportflaskfromlinuxpy.video.deviceimportDeviceapp=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!v4l2loopbackStart from scratch:#Removekernelmoduleandalldevices(noclientcanbeconnectedatthispoint)sudo modprobe -r v4l2loopback#Installsomedevicessudo modprobe v4l2loopback video_nr=20,21 card_label="Loopback 0","Loopback 1"ReferencesSee thelinux/videodev2.hheader file for details.V4L2 (Latest)(videodev.h)V4L2 6.2(videodev.h)InputAPI not documented yet. Just this example:importtimefromlinuxpy.input.deviceimportfind_gamepadspad=next(find_gamepads())abs=pad.absolutewithpad:whileTrue:print(f"X:{abs.x:>3}| Y:{abs.y:>3}| RX:{abs.rx:>3}| RY:{abs.ry:>3}",end="\r",flush=True)time.sleep(0.1)asyncio$python-masyncio>>> from linuxpy.input.device import find_gamepads>>> with next(find_gamepads()) as pad:...     async for event in pad:...         print(event)InputEvent(time=1697520475.348099, type=<EventType.SYN: 0>, code=<Synchronization.REPORT: 0>, value=0)InputEvent(time=1697520475.361564, type=<EventType.REL: 2>, code=<Relative.X: 0>, value=-1)InputEvent(time=1697520475.361564, type=<EventType.REL: 2>, code=<Relative.Y: 1>, value=1)InputEvent(time=1697520475.361564, type=<EventType.SYN: 0>, code=<Synchronization.REPORT: 0>, value=0)InputEvent(time=1697520475.371128, type=<EventType.REL: 2>, code=<Relative.X: 0>, value=-1)InputEvent(time=1697520475.371128, type=<EventType.SYN: 0>, code=<Synchronization.REPORT: 0>, value=0)...ReferencesInput (Latest)Input 6.2MIDI Sequencer$python>>> from linuxpy.midi.device import Sequencer, event_stream>>> seq = Sequencer()>>> with seq:port = seq.create_port()port.connect_from(14, 0)for event in seq:print(event)14:0   Note on              channel=0, note=100, velocity=3, off_velocity=0, duration=014:0   Clock                queue=0, pad=b''14:0   System exclusive     F0 61 62 63 F714:0   Note off             channel=0, note=55, velocity=3, off_velocity=0, duration=0asyncioasyncio is a first class citizen to linuxpy.midi:$python-masyncio>>> from linuxpy.midi.device import Sequencer, async_event_stream>>> seq = Sequencer()>>> with seq:port = seq.create_port()port.connect_from(14, 0)async for event in async_event_stream(seq):print(event)14:0   Note on              channel=0, note=100, velocity=3, off_velocity=0, duration=014:0   Clock                queue=0, pad=b''14:0   System exclusive     F0 61 62 63 F714:0   Note off             channel=0, note=55, velocity=3, off_velocity=0, duration=0CLIA basic CLI is provided that allows listing MIDI clients & ports
and dumping MIDI sequencer events:$python-mlinuxpy.midi.clilsPort   Client                   Port                     Type                           Capabilities0:0   System                   Timer                    0                              SR, W, R0:1   System                   Announce                 0                              SR, R14:0   Midi Through             Midi Through Port-0      PORT, SOFTWARE, MIDI_GENERIC   SW, SR, W, R$python-mlinuxpy.midi.clilisten0:114:00:1   Port subscribed      sender=(client=0, port=1), dest=(client=128, port=0)0:1   Port start           client=128, port=10:1   Port subscribed      sender=(client=14, port=0), dest=(client=128, port=1)0:1   Client start         client=130, port=00:1   Port start           client=130, port=00:1   Port subscribed      sender=(client=130, port=0), dest=(client=14, port=0)14:0   Note on              channel=0, note=100, velocity=3, off_velocity=0, duration=00:1   Port unsubscribed    sender=(client=130, port=0), dest=(client=14, port=0)0:1   Port exit            client=130, port=00:1   Client exit          client=130, port=00:1   Port exit            client=129, port=00:1   Client exit          client=129, port=00:1   Client start         client=129, port=00:1   Port start           client=129, port=014:0   Note on              channel=0, note=100, velocity=3, off_velocity=0, duration=014:0   Note on              channel=0, note=0, velocity=255, off_velocity=0, duration=014:0   Note on              channel=0, note=0, velocity=255, off_velocity=0, duration=0