diff --git a/objects.inv b/objects.inv
index ab8d0e9..5803021 100644
Binary files a/objects.inv and b/objects.inv differ
diff --git a/search/search_index.json b/search/search_index.json
index f2335f8..50d1aad 100644
--- a/search/search_index.json
+++ b/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to linuxpy","text":"Human 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.
Need fine control over Webcams, MIDI devices, input devices (joysticks, gamepads, keyboards, mice or even the keyboard light on your laptop)? Linuxpy has your back.
Requirements:
python >= 3.9 Fairly recent linux kernel Installed kernel modules you want to access (ex: uinput if you need user space created input devices) And yes, it is true: there are no python dependencies! Also there are no C libraries dependencies! Everything is done here through direct ioctl, read and write calls. Ain't linux wonderful?
"},{"location":"#goals","title":"Goals","text":" A pure python library (no dependency on other C libraries) No third-party python dependencies (not a hard requirement) Fine-grain access to low level linux device capabilities For video (V4L2) this means: List available 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()
"},{"location":"#installation","title":"Installation","text":"From within your favorite python environment:
pip install linuxpy pip install linuxpy To run the examples you'll need:
$ pip install linuxpy[examples]\n
To develop, run tests, build package, lint, etc you'll need:
$ pip install linuxpy[dev]\n
To run docs you'll need:
$ pip install linuxpy[docs]\n
"},{"location":"#faq","title":"FAQ","text":"Most python libraries try as hard as possible to be platform independent. Why create a library that is explicitly designed to work only on linux?
Well, first of all, one of the goals is to be able to access low level linux device capabilities like video controls. Second, I don't have access to proprietary OS like Windows or MacOS.
If this answer is not enough than think of this library as a low level dependency on linux systems of other libraries that will be concerned with providing a common API on different platforms.
"},{"location":"develop/","title":"Developers corner","text":""},{"location":"develop/#requirements","title":"Requirements","text":"A linux OS and python >= 3.9.
From within your favorite python environment:
$ pip install linuxpy[dev]\n
Additionally, to run the code generation tool you'll need castxml
installed on your system.
On a debian based run:
$ apt install castxml\n
"},{"location":"develop/#code-generation","title":"Code generation","text":"This project uses an internal simple code generator that reads linux kernel header files and produces several raw.py
ctypes based python files for each sub-system.
To re-generate these files for a newer linux kernel (or fix a bug) you'll need linux header files installed on your system + black.
To launch the tool call:
$ python -m linuxpy.codegen.cli\n
"},{"location":"develop/#running-tests","title":"Running tests","text":"Some video tests will only run with a properly configured v4l2loopback
.
$ sudo modprobe v4l2loopback video_nr=199 card_label=\"Loopback 199\"\n
Additionally the user which runs the tests will need read/write access to /dev/video199
. On most systems this can be achieved by adding the user to the video
group:
$ sudo addgroup $USER video\n
Some input tests require the user which runs the tests to have read/write access to /dev/uinput
. On most systems this can be achieved by adding the user to the input
group:
$ sudo addgroup $USER input\n
"},{"location":"api/","title":"LinuxPy reference API","text":"Here's the reference or code API, the classes, functions, parameters, attributes, and all the LinuxPy parts you can use in your applications.
If you want to learn LinuxPy you are much better off reading the LinuxPy User Guide.
"},{"location":"api/input/","title":"Input API","text":""},{"location":"api/input/#linuxpy.input.device","title":"linuxpy.input.device
","text":"Human friendly interface to linux Input subsystem.
The heart of linuxpy MIDI library is the Device
class. The recommended way is to use one of the find methods to create a Device object and use it within a context manager like:
from linuxpy.input.device import find_gamepad\n\nwith find_gamepad() as gamepad:\n print(f\"Gamepad name: {gamepad.name}\")\n
"},{"location":"api/input/#linuxpy.input.device.InputError","title":"InputError
","text":" Bases: Exception
Input error
"},{"location":"api/input/#linuxpy.input.device.Device","title":"Device(*args, **kwargs)
","text":" Bases: BaseDevice
Central linux input subsystem class.
You can create an instance directly if you know the device name:
from linuxpy.input.device import Device\n\nwith Device(\"/dev/input11\") as i11:\n print(i11.name)\n
... but it is generally easier to use the find
helper to get a device with a certain condition. Example:
from linuxpy.input.device import find\n\ntrack_point = find(name=\"TPPS/2 Elan TrackPoint\")\n
"},{"location":"api/input/#linuxpy.input.device.Device.name","title":"name: str
cached
property
","text":"The device name
"},{"location":"api/input/#linuxpy.input.device.Device.version","title":"version: Version
cached
property
","text":"The version
"},{"location":"api/input/#linuxpy.input.device.Device.physical_location","title":"physical_location: str
cached
property
","text":"The physical location
"},{"location":"api/input/#linuxpy.input.device.Device.device_id","title":"device_id: input_id
cached
property
","text":"The device input ID
"},{"location":"api/input/#linuxpy.input.device.Device.capabilities","title":"capabilities
cached
property
","text":"The device capabilities
"},{"location":"api/input/#linuxpy.input.device.Device.active_keys","title":"active_keys
property
","text":"All active keys at the moment of calling this
"},{"location":"api/input/#linuxpy.input.device.Device.x","title":"x
property
","text":"Current absolute X value
"},{"location":"api/input/#linuxpy.input.device.Device.y","title":"y
property
","text":"Current absolute Y value
"},{"location":"api/input/#linuxpy.input.device.Device.z","title":"z
property
","text":"Current absolute Z value
"},{"location":"api/input/#linuxpy.input.device.Device.rx","title":"rx
property
","text":"Current relative X value
"},{"location":"api/input/#linuxpy.input.device.Device.ry","title":"ry
property
","text":"Current relative Y value
"},{"location":"api/input/#linuxpy.input.device.Device.rz","title":"rz
property
","text":"Current relative Z value
"},{"location":"api/input/#linuxpy.input.device.Device.__iter__","title":"__iter__() -> Iterable[InputEvent]
","text":"Build an infinite iterator that streams input events You'll need an open Device before using it:
from linuxpy.input.device import find_mouse\n\nwith find_mouse() as mouse:\n for event in mouse:\n print(event)\n
"},{"location":"api/input/#linuxpy.input.device.Device.__aiter__","title":"__aiter__() -> AsyncIterable[InputEvent]
async
","text":"Build an infinite async iterator that streams input events You'll need an open Device before using it:
import asyncio\nfrom linuxpy.input.device import find_mouse\n\nasync def main():\n with find_mouse() as mouse:\n async for event in mouse:\n print(event)\n\nasyncio.run(main())\n
"},{"location":"api/input/#linuxpy.input.device.Device.get_abs_info","title":"get_abs_info(abs_code)
","text":"Absolute information for the given abs code
"},{"location":"api/input/#linuxpy.input.device.Device.read_event","title":"read_event()
","text":"Read event. Event must be available to read or otherwise will raise an error
"},{"location":"api/input/#linuxpy.input.device.EventReader","title":"EventReader(device: Device, max_queue_size=1)
","text":""},{"location":"api/input/#linuxpy.input.device.EventReader.aread","title":"aread()
async
","text":"Wait for next event or return last event
"},{"location":"api/input/#linuxpy.input.device.BaseUDevice","title":"BaseUDevice(filename=PATH, bus=Bus.USB, vendor_id=1, product_id=1, name='linuxpy emulated device')
","text":" Bases: BaseDevice
A uinput device with no capabilities registered
"},{"location":"api/input/#linuxpy.input.device.iter_input_files","title":"iter_input_files(path: PathLike = '/dev/input', pattern: str = 'event*')
","text":"List readable character devices in the given path.
"},{"location":"api/input/#linuxpy.input.device.event_batch_stream","title":"event_batch_stream(fd) -> Iterable[Sequence[InputEvent]]
","text":"Yields packets of events occurring at the same moment in time.
"},{"location":"api/input/#linuxpy.input.device.async_event_batch_stream","title":"async_event_batch_stream(fd, maxsize: int = 1000) -> AsyncIterable[Sequence[InputEvent]]
async
","text":"Yields packets of events occurring at the same moment in time.
"},{"location":"api/input/#linuxpy.input.device.find","title":"find(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first device.
If find_all is True:
The result is an iterator. Find all devices that match the criteria custom_match and kwargs. If no device is found matching the criteria it returns an empty iterator. Default is to return an iterator over all input devices found on the system.
"},{"location":"api/input/#linuxpy.input.device.find_gamepad","title":"find_gamepad(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a gamepad device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first gamepad.
If find_all is True:
The result is an iterator. Find all gamepad devices that match the criteria custom_match and kwargs. If no gamepad is found matching the criteria it returns an empty iterator. Default is to return an iterator over all gamepad devices found on the system.
"},{"location":"api/input/#linuxpy.input.device.find_keyboard","title":"find_keyboard(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a keyboard device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first keyboard.
If find_all is True:
The result is an iterator. Find all keyboard devices that match the criteria custom_match and kwargs. If no keyboard is found matching the criteria it returns an empty iterator. Default is to return an iterator over all keyboard devices found on the system.
"},{"location":"api/input/#linuxpy.input.device.find_mouse","title":"find_mouse(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a mouse device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first mouse.
If find_all is True:
The result is an iterator. Find all mouse devices that match the criteria custom_match and kwargs. If no mouse is found matching the criteria it returns an empty iterator. Default is to return an iterator over all mouse devices found on the system.
"},{"location":"api/midi/","title":"MIDI API","text":""},{"location":"api/midi/#linuxpy.midi.device","title":"linuxpy.midi.device
","text":"Human friendly interface to linux MIDI subsystem.
The heart of linuxpy MIDI library is the Sequencer
class. Usually you need only one instance of Sequencer for your application. The recommended way is to use it within a context manager like:
with Sequencer(\"My MIDI App\") as midi:\n print(f\"MIDI version: {midi.version}\")\n
which is roughly equivalent to:
midi = Sequencer(\"My MIDI App\")\nmidi.open()\ntry:\n print(f\"MIDI version: {midi.version}\")\nfinally:\n midi.close()\n
Here's a real world example:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer(\"My MIDI App\") as midi:\n print(f\"I'm client {midi.client_id}\")\n print(f\"MIDI version: {midi.version}\")\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.MidiError","title":"MidiError
","text":" Bases: Exception
MIDI error
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer","title":"Sequencer(name: str = 'linuxpy client', **kwargs)
","text":" Bases: BaseDevice
Central MIDI class.
from linuxpy.midi.device import Sequencer\n\nwith Sequencer(\"My MIDI App\") as midi:\n print(f\"I'm client {midi.client_id}\")\n print(f\"MIDI version: {midi.version}\")\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.client_info","title":"client_info: snd_seq_client_info
property
","text":"Current Client information
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.client","title":"client: Client
property
","text":"Current Client information
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.running_mode","title":"running_mode: snd_seq_running_info
property
","text":"Current running mode
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.system_info","title":"system_info: snd_seq_system_info
property
","text":"Current system information
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.iter_clients","title":"iter_clients: Iterable[Client]
property
","text":"An iterator over all open clients on the system. It returns new Client each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.clients","title":"clients: Sequence[Client]
property
","text":"Returns a new list of all clients on the system
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.iter_ports","title":"iter_ports: Iterable[Port]
property
","text":"An iterator over all open ports on the system. It returns new Port objects each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.ports","title":"ports: Sequence[Port]
property
","text":"Returns a new list of all open ports on the system. It returns new Port objects each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.__iter__","title":"__iter__() -> Iterable[Event]
","text":"Build an infinite iterator that streams MIDI events from the subscribed ports. You'll need an open sequencer before using it:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer() as midi:\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.__aiter__","title":"__aiter__() -> AsyncIterable[Event]
async
","text":"Build an infinite async iterator that streams MIDI events from the subscribed ports. You'll need an open sequencer before using it:
import asyncio\nfrom linuxpy.midi.device import Sequencer\n\nasync def main():\n with Sequencer() as midi:\n port = midi.create_port()\n port.connect_from((0, 1))\n async for event in midi:\n print(event)\n\nasyncio.run(main())\n
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.get_client","title":"get_client(client_id: int) -> Client
","text":"Returns a Client for the given ID or raises an error if the client doesn't exist. It returns new Client object each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.get_port","title":"get_port(address: FullPortAddress) -> Port
","text":"Returns a Port for the given address or raises an error if the port doesn't exist. It returns new Port object each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.create_port","title":"create_port(name: str = 'linuxpy port', capabilities: PortCapability = INPUT_OUTPUT, port_type: PortType = PortType.MIDI_GENERIC | PortType.APPLICATION) -> Port
","text":"Create a new local port. By default it will create a MIDI generic application Input/Output port.
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.delete_port","title":"delete_port(port: Union[int, Port])
","text":"Delete a previously created local port. If the port has any subscriptions they will be closed before the port is deleted
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.subscribe","title":"subscribe(src: FullPortAddress, dest: FullPortAddress)
","text":"Subscribe a source port to a destination port
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.unsubscribe","title":"unsubscribe(src: FullPortAddress, dest: FullPortAddress)
","text":"Unsubscribe a previously subscribed source port to a destination port
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.iter_raw_read","title":"iter_raw_read(max_nb_packets: int = 64) -> Iterable[Event]
","text":"Read list of pending events. If the sequencer is opened in blocking mode and there are no events it blocks until at least one event occurs otherwise as OSError is raised.
Use the read()
call instead because it handles blocking vs non-blocking variants transperently.
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.raw_read","title":"raw_read(max_nb_packets=64) -> Sequence[Event]
","text":"Read list of pending events. If there are no events it blocks until at least one event occurs and returns it.
Use the read()
call instead because it handles blocking vs non-blocking variants transperently.
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.wait_read","title":"wait_read() -> Sequence[Event]
","text":"Read list of pending events. If there are no events it blocks until at least one event occurs and returns it. This method assumes the internal file descriptior was opened in non-blocking mode.
Use the read()
call instead because it handles blocking vs non-blocking variants transperently
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.read","title":"read() -> Sequence[Event]
","text":"Read list of pending events. If there are no events it blocks until at least one event occurs and returns it
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.write","title":"write(event: Event)
","text":"Send an event message
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.send","title":"send(port: PortAddress, event_type: Union[str, int, EventType], queue: int = QUEUE_DIRECT, to: Union[FullPortAddress, FullPortAddresses] = SUBSCRIBERS, **kwargs)
","text":"Send a message of the given type from a specific port to the destination address(es). Use kwargs to pass specific event arguments like velocity in a \"note on\" event.
event_type can be an instance of EventType or the equivalent number or a case insensitive string matching the event type (ex: \"noteon\", \"NOTEON\", \"note-on\" or \"note on\").
The following example sends \"note on\" with velocity 45 on port 0 of client 14:
midi.send((14, 0), \"note on\", velocity=45)\n
"},{"location":"api/midi/#linuxpy.midi.device.Client","title":"Client(sequencer: Sequencer, client: snd_seq_client_info)
","text":"MIDI sequencer client. Don't instantiate this object directly Use instead Sequencer.get_client()
"},{"location":"api/midi/#linuxpy.midi.device.Client.name","title":"name: str
property
","text":"Client name
"},{"location":"api/midi/#linuxpy.midi.device.Client.is_local","title":"is_local: bool
property
","text":"True if the client was created by the MIDI sequencer that it references or False otherwise\"
"},{"location":"api/midi/#linuxpy.midi.device.Client.iter_ports","title":"iter_ports: Iterable[Port]
property
","text":"An iterator over all open ports for this client. It returns new Port each time
"},{"location":"api/midi/#linuxpy.midi.device.Client.ports","title":"ports: Sequence[Port]
property
","text":"Returns a new list of all open ports for this client
"},{"location":"api/midi/#linuxpy.midi.device.Client.__int__","title":"__int__()
","text":"The client ID
"},{"location":"api/midi/#linuxpy.midi.device.Port","title":"Port(sequencer: Sequencer, port: snd_seq_port_info)
","text":"MIDI sequencer port. Don't instantiate this object directly Use instead Sequencer.get_port()
"},{"location":"api/midi/#linuxpy.midi.device.Port.name","title":"name: str
property
","text":"Port name
"},{"location":"api/midi/#linuxpy.midi.device.Port.is_local","title":"is_local: bool
property
","text":"True if the port was created by the MIDI sequencer that it references or False otherwise\"
"},{"location":"api/midi/#linuxpy.midi.device.Port.client_id","title":"client_id: int
property
","text":"The client ID
"},{"location":"api/midi/#linuxpy.midi.device.Port.port_id","title":"port_id: int
property
","text":"The port ID
"},{"location":"api/midi/#linuxpy.midi.device.Port.type","title":"type: PortType
property
","text":"The port type
"},{"location":"api/midi/#linuxpy.midi.device.Port.capability","title":"capability: PortCapability
property
","text":"The port capabilities
"},{"location":"api/midi/#linuxpy.midi.device.Port.address","title":"address: snd_seq_addr
property
","text":"The port address
"},{"location":"api/midi/#linuxpy.midi.device.Port.__int__","title":"__int__()
","text":"The port ID
"},{"location":"api/midi/#linuxpy.midi.device.Port.connect_from","title":"connect_from(src: FullPortAddress)
","text":"Connect this port to a remote port. After connecting, this port will receive events originating from the source port.
Example:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer() as midi:\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.Port.disconnect_from","title":"disconnect_from(src: FullPortAddress)
","text":"Disconnect this port from a previously connected source port.
"},{"location":"api/midi/#linuxpy.midi.device.Port.connect_to","title":"connect_to(dest: FullPortAddress)
","text":"Connect this port to a remote port. After connecting, events originating from this port will be sent to the destination port.
Example:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer() as midi:\n port = midi.create_port()\n # Assume 14:0 is Midi Through\n port.connect_to((14, 0))\n port.send(\"note on\", note=11, velocity=10)\n
"},{"location":"api/midi/#linuxpy.midi.device.Port.disconnect_to","title":"disconnect_to(dest: FullPortAddress)
","text":"Disconnect this port from a previously connected destination port.
"},{"location":"api/midi/#linuxpy.midi.device.Port.delete","title":"delete()
","text":"Delete this port. Raises MidiError if port is not local. Any subscriptions are canceled before the port is deleted.
"},{"location":"api/midi/#linuxpy.midi.device.Port.send","title":"send(event_type: Union[str, int, EventType], **kwargs)
","text":"Send a message of the given type from to the destination address(es). Use kwargs to pass specific event arguments like velocity in a \"note on\" event.
event_type can be an instance of EventType or the equivalent number or a case insensitive string matching the event type (ex: \"noteon\", \"NOTEON\", \"note-on\" or \"note on\").
The following example sends \"note on\" on note 42, with velocity 45:
port.send(\"note on\", note=42, velocity=45)\n
"},{"location":"api/midi/#linuxpy.midi.device.Event","title":"Event(event: snd_seq_event)
","text":"Event message object result of listening on a sequencer
"},{"location":"api/midi/#linuxpy.midi.device.Event.__bytes__","title":"__bytes__()
","text":"Serialize the Event in a bytes ready to be sent
"},{"location":"api/midi/#linuxpy.midi.device.Event.new","title":"new(etype: EventT, **kwargs)
classmethod
","text":"Create new Event of the given type
"},{"location":"api/midi/#linuxpy.midi.device.to_address","title":"to_address(addr: FullPortAddress) -> snd_seq_addr
","text":"Convert to low level snd_seq_addr
"},{"location":"api/midi/#linuxpy.midi.device.event_stream","title":"event_stream(sequencer: Sequencer) -> Iterable[Event]
","text":"Infinite stream of events coming from the given sequencer
"},{"location":"api/midi/#linuxpy.midi.device.async_event_stream","title":"async_event_stream(sequencer: Sequencer, maxsize: int = 10) -> AsyncIterable[Event]
async
","text":"Infinite async stream of events coming from the given sequencer
"},{"location":"api/video/","title":"Video API","text":""},{"location":"api/video/#linuxpy.video.device","title":"linuxpy.video.device
","text":"Human friendly interface to V4L2 (Video 4 Linux 2) subsystem.
"},{"location":"api/video/#linuxpy.video.device.V4L2Error","title":"V4L2Error
","text":" Bases: Exception
Video for linux 2 error
"},{"location":"api/video/#linuxpy.video.device.Frame","title":"Frame(data: bytes, buff: raw.v4l2_buffer, format: Format)
","text":"The resulting object from an acquisition.
"},{"location":"api/video/#linuxpy.video.device.EventReader","title":"EventReader(device: Device, max_queue_size=100)
","text":""},{"location":"api/video/#linuxpy.video.device.EventReader.aread","title":"aread()
async
","text":"Wait for next event or return last event in queue
"},{"location":"api/video/#linuxpy.video.device.FrameReader","title":"FrameReader(device: Device, raw_read: Callable[[], Buffer], max_queue_size: int = 1)
","text":""},{"location":"api/video/#linuxpy.video.device.FrameReader.aread","title":"aread() -> Frame
async
","text":"Wait for next frame or return last frame
"},{"location":"api/video/#linuxpy.video.device.create_buffer","title":"create_buffer(fd, buffer_type: BufferType, memory: Memory) -> raw.v4l2_buffer
","text":"request + query buffers
"},{"location":"api/video/#linuxpy.video.device.create_buffers","title":"create_buffers(fd, buffer_type: BufferType, memory: Memory, count: int) -> list[raw.v4l2_buffer]
","text":"request + query buffers
"},{"location":"api/video/#linuxpy.video.device.create_mmap_buffers","title":"create_mmap_buffers(fd, buffer_type: BufferType, memory: Memory, count: int) -> list[mmap.mmap]
","text":"create buffers + mmap_from_buffer
"},{"location":"api/video/#linuxpy.video.device.iter_video_output_files","title":"iter_video_output_files(path: PathLike = '/dev') -> Iterable[Path]
","text":"Some drivers (ex: v4l2loopback) don't report being output capable so that apps like zoom recognize them as valid capture devices so some results might be missing
"},{"location":"user_guide/","title":"User guide","text":"This tutorial shows you how to use LinuxPy with most of its features.
"},{"location":"user_guide/input/","title":"Input","text":"Human friendly interface to the Linux Input subsystem.
API not documented yet. Just this example:
import time\nfrom linuxpy.input.device import find_gamepads\n\npad = next(find_gamepads())\nabs = pad.absolute\n\nwith pad:\n while True:\n print(f\"X:{abs.x:>3} | Y:{abs.y:>3} | RX:{abs.rx:>3} | RY:{abs.ry:>3}\", end=\"\\r\", flush=True)\n time.sleep(0.1)\n
"},{"location":"user_guide/input/#asyncio","title":"asyncio","text":"python -m asyncio from linuxpy.input.device import find_gamepad with find_gamepad() 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) ..."},{"location":"user_guide/input/#references","title":"References","text":""},{"location":"user_guide/midi/","title":"MIDI Sequencer","text":"Without further ado:
python from linuxpy.midi.device import Sequencer with Sequencer() as 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=0 14:0 Clock queue=0, pad=b'' 14:0 System exclusive F0 61 62 63 F7 14:0 Note off channel=0, note=55, velocity=3, off_velocity=0, duration=0"},{"location":"user_guide/midi/#system-information","title":"System information","text":"$ python\n>>> from linuxpy.midi.device import Sequencer\n>>> seq = Sequencer(\"a midi client\")\n>>> seq.open()\n\n>>> seq.version\n1.0.2\n\n>>> seq.client_info\nsnd_seq_client_info(client=128, type=1, name=b'a midi client', filter=0, multicast_filter=b'', event_filter=b'', num_ports=0, event_lost=0, card=-1, pid=1288570)\n\n>>> seq.running_mode\nsnd_seq_running_info(client=0, big_endian=0, cpu_mode=0, pad=0)\n\n>>> seq.system_info\nsnd_seq_system_info(queues=32, clients=192, ports=254, channels=256, cur_clients=3, cur_queues=0)\n
"},{"location":"user_guide/midi/#asyncio","title":"asyncio","text":"asyncio is a first class citizen to linuxpy.midi:
$ python -m asyncio\n\n>>> from linuxpy.midi.device import Sequencer\n>>> with Sequencer() as seq:\n... port = seq.create_port()\n... port.connect_from(14, 0)\n... async for event in seq:\n... print(event)\n 14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0\n 14:0 Clock queue=0, pad=b''\n 14:0 System exclusive F0 61 62 63 F7\n 14:0 Note off channel=0, note=55, velocity=3, off_velocity=0, duration=0\n
"},{"location":"user_guide/midi/#cli","title":"CLI","text":"A basic CLI is provided that allows listing MIDI clients & ports and dumping MIDI sequencer events:
List all ports:
python -m linuxpy.midi.cli ls Port Client Port Type Capabilities 0:0 System Timer 0 SR, W, R 0:1 System Announce 0 SR, R 14:0 Midi Through Midi Through Port-0 PORT, SOFTWARE, MIDI_GENERIC SW, SR, W, R Listen to events on selected port(s):
python -m linuxpy.midi.cli listen 0:1 14:0 0:1 Port subscribed sender=(client=0, port=1), dest=(client=128, port=0) 0:1 Port start client=128, port=1 0:1 Port subscribed sender=(client=14, port=0), dest=(client=128, port=1) 0:1 Client start client=130, port=0 0:1 Port start client=130, port=0 0: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=0 0:1 Port unsubscribed sender=(client=130, port=0), dest=(client=14, port=0) 0:1 Port exit client=130, port=0 0:1 Client exit client=130, port=0 0:1 Port exit client=129, port=0 0:1 Client exit client=129, port=0 0:1 Client start client=129, port=0 0:1 Port start client=129, port=0 14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0 14:0 Note on channel=0, note=0, velocity=255, off_velocity=0, duration=0 14:0 Note on channel=0, note=0, velocity=255, off_velocity=0, duration=0"},{"location":"user_guide/video/","title":"Video","text":"Human friendly interface to the Video for Linux 2 (V4L2) subsystem.
Without further ado:
python from linuxpy.video.device import Device with Device.from_id(0) as cam: for i, frame in enumerate(cam): print(f\"frame #{i}: {len(frame)} bytes\") 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 ..."},{"location":"user_guide/video/#device-creation","title":"Device creation","text":"Create a device object from an ID:
from linuxpy.video.device import Device\ncamera = Device.from_id(10)\n
from a filename:
from linuxpy.video.device import Device\ncamera = Device(\"/dev/video10\")\n
or from an existing file object:
from linuxpy.video.device import Device\nwith open(\"/dev/video10\", \"rb+\", buffering=0) as fd:\n camera = Device(fd)\n
Before using video Device
object you need to open it (except in the example directly above when creating a device from a file object). You can either use the device object as a context manager (prefered):
with Device.from_id(10) as camera:\n ...\n
The Device object is a reusable, reentrant but not thread safe context manager. This means that Device object can not only be used in multiple with statements, but may also be used inside a with statement that is already using the same context manager.
So the following examples will work just fine:
with Device.from_id(10) as camera:\n ...\n with camera:\n ...\n\nwith camera:\n ...\n
Alternatively, you can manage calls Device.open()
/Device.close()
manually:
camera = Device.from_id(10)\ncamera.open()\ntry:\n ...\nfinally:\n camera.close()\n
"},{"location":"user_guide/video/#capture","title":"Capture","text":"Simple capture without any configuration is possible using the Device object as an infinite iterator:
from linuxpy.video.device import Device, VideoCapture\n\nwith Device.from_id(0) as camera:\n for frame in camera:\n ...\n
The resulting Frame
objects can safely and efficiently be converted to bytes.
To be able to configure the acquisition, you will need to use the VideoCapture
helper. Here is an example with image size and format configuration:
from linuxpy.video.device import Device, VideoCapture\n\nwith Device.from_id(0) as camera:\n capture = VideoCapture(camera)\n capture.set_format(640, 480, \"MJPG\")\n with capture:\n for frame in capture:\n ...\n
Note that VideoCapture
configuration must be done before the capture is started (ie, the with capture:
statement.)
By default, VideoCapture will use memory map if the device has STREAMING capability and falls back to standard read if not. It is also possible to force a specific reader:
from linuxpy.video.device import Capability, Device, VideoCapture\n\nwith Device.from_id(0) as cam:\n with VideoCapture(cam, source=Capability.READWRITE):\n for frame in capture:\n ...\n
"},{"location":"user_guide/video/#information","title":"Information","text":"Getting information about the device:
>>> from linuxpy.video.device import Device, BufferType\n\n>>> cam = Device.from_id(0)\n>>> cam.open()\n>>> cam.info.card\n'Integrated_Webcam_HD: Integrate'\n\n>>> cam.info.capabilities\n<Capability.STREAMING|EXT_PIX_FORMAT|VIDEO_CAPTURE: 69206017>\n\n>>> cam.info.formats\n[ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'Motion-JPEG',\n flags=<ImageFormatFlag.COMPRESSED: 1>, pixelformat=<PixelFormat.MJPEG: 1196444237>),\n ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'YUYV 4:2:2',\n flags=<ImageFormatFlag.0: 0>, pixelformat=<PixelFormat.YUYV: 1448695129>)]\n\n>>> cam.get_format(BufferType.VIDEO_CAPTURE)\nFormat(width=640, height=480, pixelformat=<PixelFormat.MJPEG: 1196444237>}\n\n>>> for ctrl in cam.controls.values(): print(ctrl)\n<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>\n<IntegerControl contrast min=0 max=255 step=1 default=32 value=32>\n<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>\n<IntegerControl hue min=-180 max=180 step=1 default=0 value=0>\n<BooleanControl white_balance_automatic default=True value=True>\n<IntegerControl gamma min=90 max=150 step=1 default=120 value=120>\n<MenuControl power_line_frequency default=1 value=1>\n<IntegerControl white_balance_temperature min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive>\n<IntegerControl sharpness min=0 max=7 step=1 default=2 value=2>\n<IntegerControl backlight_compensation min=0 max=2 step=1 default=1 value=1>\n<MenuControl auto_exposure default=3 value=3>\n<IntegerControl exposure_time_absolute min=4 max=1250 step=1 default=156 value=156 flags=inactive>\n<BooleanControl exposure_dynamic_framerate default=False value=False>\n\n>>> cam.controls[\"saturation\"]\n<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>\n\n>>> cam.controls[\"saturation\"].id\n9963778\n>>> cam.controls[9963778]\n<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>\n\n>>> cam.controls.brightness\n<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>\n>>> cam.controls.brightness.value = 64\n>>> cam.controls.brightness\n<IntegerControl brightness min=0 max=255 step=1 default=128 value=64>\n
(see also v4l2py-ctl example)
"},{"location":"user_guide/video/#asyncio","title":"asyncio","text":"linuxpy.video is asyncio friendly:
python -m asyncio from linuxpy.video.device import Device with Device.from_id(0) as cam: async for frame in cam: print(f\"frame {len(frame)}\") frame 10224 frame 10304 frame 10136 ... (check basic async and web async examples)
"},{"location":"user_guide/video/#gevent","title":"gevent","text":"linuxpy.video is also gevent friendly:
>>> from linuxpy.io import GeventIO\n>>> from linuxpy.video.device import Device\n>>> 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
(check basic gevent and web gevent examples)
"},{"location":"user_guide/video/#video-output","title":"Video output","text":"It 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\n>>> dev_source = Device.from_id(0)\n>>> dev_sink = Device.from_id(10)\n>>> with dev_source, dev_target:\n>>> source = VideoCapture(dev_source)\n>>> sink = VideoOutput(dev_sink)\n>>> source.set_format(640, 480, \"MJPG\")\n>>> sink.set_format(640, 480, \"MJPG\")\n>>> with source, sink:\n>>> for frame in source:\n>>> sink.write(frame.data)\n
By default, VideoOutput will use memory map if the device has STREAMING capability and falls back to standard write if not. It is also possible to force a specific writer with VideoOutput(cam, sink=Capability.READWRITE)
:
"},{"location":"user_guide/video/#v4l2loopback","title":"v4l2loopback","text":"This is just an example on how to setup v4l2loopback.
Start from scratch:
# Remove kernel module and all devices (no client can be connected at this point)\nsudo modprobe -r v4l2loopback\n\n# Install some devices\nsudo modprobe v4l2loopback video_nr=20,21 card_label=\"Loopback 0\",\"Loopback 1\"\n
"},{"location":"user_guide/video/#references","title":"References","text":"See the linux/videodev2.h
header file for details.
V4L2 (Latest) (videodev.h) V4L2 6.2 (videodev.h) "}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to linuxpy","text":"Human 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.
Need fine control over Webcams, MIDI devices, input devices (joysticks, gamepads, keyboards, mice or even the keyboard light on your laptop)? Linuxpy has your back.
Requirements:
python >= 3.9 Fairly recent linux kernel Installed kernel modules you want to access (ex: uinput if you need user space created input devices) And yes, it is true: there are no python dependencies! Also there are no C libraries dependencies! Everything is done here through direct ioctl, read and write calls. Ain't linux wonderful?
"},{"location":"#goals","title":"Goals","text":" A pure python library (no dependency on other C libraries) No third-party python dependencies (not a hard requirement) Fine-grain access to low level linux device capabilities For video (V4L2) this means: List available 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()
"},{"location":"#installation","title":"Installation","text":"From within your favorite python environment:
pip install linuxpy pip install linuxpy To run the examples you'll need:
$ pip install linuxpy[examples]\n
To develop, run tests, build package, lint, etc you'll need:
$ pip install linuxpy[dev]\n
To run docs you'll need:
$ pip install linuxpy[docs]\n
"},{"location":"#faq","title":"FAQ","text":"Most python libraries try as hard as possible to be platform independent. Why create a library that is explicitly designed to work only on linux?
Well, first of all, one of the goals is to be able to access low level linux device capabilities like video controls. Second, I don't have access to proprietary OS like Windows or MacOS.
If this answer is not enough than think of this library as a low level dependency on linux systems of other libraries that will be concerned with providing a common API on different platforms.
"},{"location":"develop/","title":"Developers corner","text":""},{"location":"develop/#requirements","title":"Requirements","text":"A linux OS and python >= 3.9.
From within your favorite python environment:
$ pip install linuxpy[dev]\n
Additionally, to run the code generation tool you'll need castxml
installed on your system.
On a debian based run:
$ apt install castxml\n
"},{"location":"develop/#code-generation","title":"Code generation","text":"This project uses an internal simple code generator that reads linux kernel header files and produces several raw.py
ctypes based python files for each sub-system.
To re-generate these files for a newer linux kernel (or fix a bug) you'll need linux header files installed on your system + black.
To launch the tool call:
$ python -m linuxpy.codegen.cli\n
"},{"location":"develop/#running-tests","title":"Running tests","text":"Some video tests will only run with a properly configured v4l2loopback
.
$ sudo modprobe v4l2loopback video_nr=199 card_label=\"Loopback 199\"\n
Additionally the user which runs the tests will need read/write access to /dev/video199
. On most systems this can be achieved by adding the user to the video
group:
$ sudo addgroup $USER video\n
Some input tests require the user which runs the tests to have read/write access to /dev/uinput
. On most systems this can be achieved by adding the user to the input
group:
$ sudo addgroup $USER input\n
"},{"location":"api/","title":"LinuxPy reference API","text":"Here's the reference or code API, the classes, functions, parameters, attributes, and all the LinuxPy parts you can use in your applications.
If you want to learn LinuxPy you are much better off reading the LinuxPy User Guide.
"},{"location":"api/input/","title":"Input API","text":""},{"location":"api/input/#linuxpy.input.device","title":"linuxpy.input.device
","text":"Human friendly interface to linux Input subsystem.
The heart of linuxpy MIDI library is the Device
class. The recommended way is to use one of the find methods to create a Device object and use it within a context manager like:
from linuxpy.input.device import find_gamepad\n\nwith find_gamepad() as gamepad:\n print(f\"Gamepad name: {gamepad.name}\")\n
"},{"location":"api/input/#linuxpy.input.device.InputError","title":"InputError
","text":" Bases: Exception
Input error
"},{"location":"api/input/#linuxpy.input.device.Event","title":"Event(event: input_event)
","text":"Event generated by an input device
"},{"location":"api/input/#linuxpy.input.device.Event.timestamp","title":"timestamp: float
property
","text":"The timestamp associated with the event
"},{"location":"api/input/#linuxpy.input.device.Event.type","title":"type: EventType
property
","text":"The type of event
"},{"location":"api/input/#linuxpy.input.device.Event.code","title":"code
property
","text":"The event code
"},{"location":"api/input/#linuxpy.input.device.Event.value","title":"value: int
property
","text":"The event value
"},{"location":"api/input/#linuxpy.input.device.Device","title":"Device(*args, **kwargs)
","text":" Bases: BaseDevice
Central linux input subsystem class.
You can create an instance directly if you know the device name:
from linuxpy.input.device import Device\n\nwith Device(\"/dev/input11\") as i11:\n print(i11.name)\n
... but it is generally easier to use the find
helper to get a device with a certain condition. Example:
from linuxpy.input.device import find\n\ntrack_point = find(name=\"TPPS/2 Elan TrackPoint\")\n
"},{"location":"api/input/#linuxpy.input.device.Device.name","title":"name: str
cached
property
","text":"The device name
"},{"location":"api/input/#linuxpy.input.device.Device.version","title":"version: Version
cached
property
","text":"The version
"},{"location":"api/input/#linuxpy.input.device.Device.physical_location","title":"physical_location: str
cached
property
","text":"The physical location
"},{"location":"api/input/#linuxpy.input.device.Device.device_id","title":"device_id: input_id
cached
property
","text":"The device input ID
"},{"location":"api/input/#linuxpy.input.device.Device.capabilities","title":"capabilities
cached
property
","text":"The device capabilities
"},{"location":"api/input/#linuxpy.input.device.Device.active_keys","title":"active_keys
property
","text":"All active keys at the moment of calling this
"},{"location":"api/input/#linuxpy.input.device.Device.x","title":"x
property
","text":"Current absolute X value
"},{"location":"api/input/#linuxpy.input.device.Device.y","title":"y
property
","text":"Current absolute Y value
"},{"location":"api/input/#linuxpy.input.device.Device.z","title":"z
property
","text":"Current absolute Z value
"},{"location":"api/input/#linuxpy.input.device.Device.rx","title":"rx
property
","text":"Current relative X value
"},{"location":"api/input/#linuxpy.input.device.Device.ry","title":"ry
property
","text":"Current relative Y value
"},{"location":"api/input/#linuxpy.input.device.Device.rz","title":"rz
property
","text":"Current relative Z value
"},{"location":"api/input/#linuxpy.input.device.Device.__iter__","title":"__iter__() -> Iterable[Event]
","text":"Build an infinite iterator that streams input events. You'll need an open Device before using it:
from linuxpy.input.device import find_mouse\n\nwith find_mouse() as mouse:\n for event in mouse:\n print(event)\n
"},{"location":"api/input/#linuxpy.input.device.Device.__aiter__","title":"__aiter__() -> AsyncIterable[Event]
async
","text":"Build an infinite async iterator that streams input events. You'll need an open Device before using it:
import asyncio\nfrom linuxpy.input.device import find_mouse\n\nasync def main():\n with find_mouse() as mouse:\n async for event in mouse:\n print(event)\n\nasyncio.run(main())\n
"},{"location":"api/input/#linuxpy.input.device.Device.get_abs_info","title":"get_abs_info(abs_code)
","text":"Absolute information for the given abs code
"},{"location":"api/input/#linuxpy.input.device.Device.read_event","title":"read_event()
","text":"Read event. Event must be available to read or otherwise will raise an error
"},{"location":"api/input/#linuxpy.input.device.Device.grab","title":"grab()
","text":"Grab the device for exclusive use
"},{"location":"api/input/#linuxpy.input.device.Device.ungrab","title":"ungrab()
","text":"Release (ungrab) the device
"},{"location":"api/input/#linuxpy.input.device.Grab","title":"Grab(device: Device)
","text":"Context manager which grabs the device on enter and releases (ungrabs) it on exit.
The device should be open for operation before the object is called on the with
statement.
This context manager is reusable but not reentrant and not thread safe.
Example:
from linuxpy.input.device import find_mouse, Grab\nwith find_mouse() as mouse:\n with Grab(mouse):\n print(mouse.active_keys)\n
"},{"location":"api/input/#linuxpy.input.device.BaseUDevice","title":"BaseUDevice(filename=PATH, bus=Bus.VIRTUAL, vendor_id=1, product_id=1, name='linuxpy emulated device')
","text":" Bases: BaseDevice
A uinput device with no capabilities registered
"},{"location":"api/input/#linuxpy.input.device.iter_input_files","title":"iter_input_files(path: PathLike = '/dev/input', pattern: str = 'event*')
","text":"List readable character devices in the given path.
"},{"location":"api/input/#linuxpy.input.device.event_batch_stream","title":"event_batch_stream(fd) -> Iterable[Sequence[Event]]
","text":"Yields packets of events occurring at the same moment in time.
"},{"location":"api/input/#linuxpy.input.device.async_event_batch_stream","title":"async_event_batch_stream(fd, maxsize: int = 1000) -> AsyncIterable[Sequence[Event]]
async
","text":"Yields packets of events occurring at the same moment in time.
"},{"location":"api/input/#linuxpy.input.device.find","title":"find(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first device.
If find_all is True:
The result is an iterator. Find all devices that match the criteria custom_match and kwargs. If no device is found matching the criteria it returns an empty iterator. Default is to return an iterator over all input devices found on the system.
"},{"location":"api/input/#linuxpy.input.device.find_gamepad","title":"find_gamepad(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a gamepad device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first gamepad.
If find_all is True:
The result is an iterator. Find all gamepad devices that match the criteria custom_match and kwargs. If no gamepad is found matching the criteria it returns an empty iterator. Default is to return an iterator over all gamepad devices found on the system.
"},{"location":"api/input/#linuxpy.input.device.find_keyboard","title":"find_keyboard(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a keyboard device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first keyboard.
If find_all is True:
The result is an iterator. Find all keyboard devices that match the criteria custom_match and kwargs. If no keyboard is found matching the criteria it returns an empty iterator. Default is to return an iterator over all keyboard devices found on the system.
"},{"location":"api/input/#linuxpy.input.device.find_mouse","title":"find_mouse(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a mouse device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first mouse.
If find_all is True:
The result is an iterator. Find all mouse devices that match the criteria custom_match and kwargs. If no mouse is found matching the criteria it returns an empty iterator. Default is to return an iterator over all mouse devices found on the system.
"},{"location":"api/midi/","title":"MIDI API","text":""},{"location":"api/midi/#linuxpy.midi.device","title":"linuxpy.midi.device
","text":"Human friendly interface to linux MIDI subsystem.
The heart of linuxpy MIDI library is the Sequencer
class. Usually you need only one instance of Sequencer for your application. The recommended way is to use it within a context manager like:
with Sequencer(\"My MIDI App\") as midi:\n print(f\"MIDI version: {midi.version}\")\n
which is roughly equivalent to:
midi = Sequencer(\"My MIDI App\")\nmidi.open()\ntry:\n print(f\"MIDI version: {midi.version}\")\nfinally:\n midi.close()\n
Here's a real world example:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer(\"My MIDI App\") as midi:\n print(f\"I'm client {midi.client_id}\")\n print(f\"MIDI version: {midi.version}\")\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.MidiError","title":"MidiError
","text":" Bases: Exception
MIDI error
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer","title":"Sequencer(name: str = 'linuxpy client', **kwargs)
","text":" Bases: BaseDevice
Central MIDI class.
from linuxpy.midi.device import Sequencer\n\nwith Sequencer(\"My MIDI App\") as midi:\n print(f\"I'm client {midi.client_id}\")\n print(f\"MIDI version: {midi.version}\")\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.client_info","title":"client_info: snd_seq_client_info
property
","text":"Current Client information
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.client","title":"client: Client
property
","text":"Current Client information
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.running_mode","title":"running_mode: snd_seq_running_info
property
","text":"Current running mode
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.system_info","title":"system_info: snd_seq_system_info
property
","text":"Current system information
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.iter_clients","title":"iter_clients: Iterable[Client]
property
","text":"An iterator over all open clients on the system. It returns new Client each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.clients","title":"clients: Sequence[Client]
property
","text":"Returns a new list of all clients on the system
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.iter_ports","title":"iter_ports: Iterable[Port]
property
","text":"An iterator over all open ports on the system. It returns new Port objects each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.ports","title":"ports: Sequence[Port]
property
","text":"Returns a new list of all open ports on the system. It returns new Port objects each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.__iter__","title":"__iter__() -> Iterable[Event]
","text":"Build an infinite iterator that streams MIDI events from the subscribed ports. You'll need an open sequencer before using it:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer() as midi:\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.__aiter__","title":"__aiter__() -> AsyncIterable[Event]
async
","text":"Build an infinite async iterator that streams MIDI events from the subscribed ports. You'll need an open sequencer before using it:
import asyncio\nfrom linuxpy.midi.device import Sequencer\n\nasync def main():\n with Sequencer() as midi:\n port = midi.create_port()\n port.connect_from((0, 1))\n async for event in midi:\n print(event)\n\nasyncio.run(main())\n
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.get_client","title":"get_client(client_id: int) -> Client
","text":"Returns a Client for the given ID or raises an error if the client doesn't exist. It returns new Client object each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.get_port","title":"get_port(address: FullPortAddress) -> Port
","text":"Returns a Port for the given address or raises an error if the port doesn't exist. It returns new Port object each time
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.create_port","title":"create_port(name: str = 'linuxpy port', capabilities: PortCapability = INPUT_OUTPUT, port_type: PortType = PortType.MIDI_GENERIC | PortType.APPLICATION) -> Port
","text":"Create a new local port. By default it will create a MIDI generic application Input/Output port.
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.delete_port","title":"delete_port(port: Union[int, Port])
","text":"Delete a previously created local port. If the port has any subscriptions they will be closed before the port is deleted
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.subscribe","title":"subscribe(src: FullPortAddress, dest: FullPortAddress)
","text":"Subscribe a source port to a destination port
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.unsubscribe","title":"unsubscribe(src: FullPortAddress, dest: FullPortAddress)
","text":"Unsubscribe a previously subscribed source port to a destination port
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.iter_raw_read","title":"iter_raw_read(max_nb_packets: int = 64) -> Iterable[Event]
","text":"Read list of pending events. If the sequencer is opened in blocking mode and there are no events it blocks until at least one event occurs otherwise as OSError is raised.
Use the read()
call instead because it handles blocking vs non-blocking variants transperently.
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.raw_read","title":"raw_read(max_nb_packets=64) -> Sequence[Event]
","text":"Read list of pending events. If there are no events it blocks until at least one event occurs and returns it.
Use the read()
call instead because it handles blocking vs non-blocking variants transperently.
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.wait_read","title":"wait_read() -> Sequence[Event]
","text":"Read list of pending events. If there are no events it blocks until at least one event occurs and returns it. This method assumes the internal file descriptior was opened in non-blocking mode.
Use the read()
call instead because it handles blocking vs non-blocking variants transperently
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.read","title":"read() -> Sequence[Event]
","text":"Read list of pending events. If there are no events it blocks until at least one event occurs and returns it
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.write","title":"write(event: Event)
","text":"Send an event message
"},{"location":"api/midi/#linuxpy.midi.device.Sequencer.send","title":"send(port: PortAddress, event_type: Union[str, int, EventType], queue: int = QUEUE_DIRECT, to: Union[FullPortAddress, FullPortAddresses] = SUBSCRIBERS, **kwargs)
","text":"Send a message of the given type from a specific port to the destination address(es). Use kwargs to pass specific event arguments like velocity in a \"note on\" event.
event_type can be an instance of EventType or the equivalent number or a case insensitive string matching the event type (ex: \"noteon\", \"NOTEON\", \"note-on\" or \"note on\").
The following example sends \"note on\" with velocity 45 on port 0 of client 14:
midi.send((14, 0), \"note on\", velocity=45)\n
"},{"location":"api/midi/#linuxpy.midi.device.Client","title":"Client(sequencer: Sequencer, client: snd_seq_client_info)
","text":"MIDI sequencer client. Don't instantiate this object directly Use instead Sequencer.get_client()
"},{"location":"api/midi/#linuxpy.midi.device.Client.name","title":"name: str
property
","text":"Client name
"},{"location":"api/midi/#linuxpy.midi.device.Client.is_local","title":"is_local: bool
property
","text":"True if the client was created by the MIDI sequencer that it references or False otherwise\"
"},{"location":"api/midi/#linuxpy.midi.device.Client.iter_ports","title":"iter_ports: Iterable[Port]
property
","text":"An iterator over all open ports for this client. It returns new Port each time
"},{"location":"api/midi/#linuxpy.midi.device.Client.ports","title":"ports: Sequence[Port]
property
","text":"Returns a new list of all open ports for this client
"},{"location":"api/midi/#linuxpy.midi.device.Client.__int__","title":"__int__()
","text":"The client ID
"},{"location":"api/midi/#linuxpy.midi.device.Port","title":"Port(sequencer: Sequencer, port: snd_seq_port_info)
","text":"MIDI sequencer port. Don't instantiate this object directly Use instead Sequencer.get_port()
"},{"location":"api/midi/#linuxpy.midi.device.Port.name","title":"name: str
property
","text":"Port name
"},{"location":"api/midi/#linuxpy.midi.device.Port.is_local","title":"is_local: bool
property
","text":"True if the port was created by the MIDI sequencer that it references or False otherwise\"
"},{"location":"api/midi/#linuxpy.midi.device.Port.client_id","title":"client_id: int
property
","text":"The client ID
"},{"location":"api/midi/#linuxpy.midi.device.Port.port_id","title":"port_id: int
property
","text":"The port ID
"},{"location":"api/midi/#linuxpy.midi.device.Port.type","title":"type: PortType
property
","text":"The port type
"},{"location":"api/midi/#linuxpy.midi.device.Port.capability","title":"capability: PortCapability
property
","text":"The port capabilities
"},{"location":"api/midi/#linuxpy.midi.device.Port.address","title":"address: snd_seq_addr
property
","text":"The port address
"},{"location":"api/midi/#linuxpy.midi.device.Port.__int__","title":"__int__()
","text":"The port ID
"},{"location":"api/midi/#linuxpy.midi.device.Port.connect_from","title":"connect_from(src: FullPortAddress)
","text":"Connect this port to a remote port. After connecting, this port will receive events originating from the source port.
Example:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer() as midi:\n port = midi.create_port()\n port.connect_from((0, 1))\n for event in midi:\n print(event)\n
"},{"location":"api/midi/#linuxpy.midi.device.Port.disconnect_from","title":"disconnect_from(src: FullPortAddress)
","text":"Disconnect this port from a previously connected source port.
"},{"location":"api/midi/#linuxpy.midi.device.Port.connect_to","title":"connect_to(dest: FullPortAddress)
","text":"Connect this port to a remote port. After connecting, events originating from this port will be sent to the destination port.
Example:
from linuxpy.midi.device import Sequencer\n\nwith Sequencer() as midi:\n port = midi.create_port()\n # Assume 14:0 is Midi Through\n port.connect_to((14, 0))\n port.send(\"note on\", note=11, velocity=10)\n
"},{"location":"api/midi/#linuxpy.midi.device.Port.disconnect_to","title":"disconnect_to(dest: FullPortAddress)
","text":"Disconnect this port from a previously connected destination port.
"},{"location":"api/midi/#linuxpy.midi.device.Port.delete","title":"delete()
","text":"Delete this port. Raises MidiError if port is not local. Any subscriptions are canceled before the port is deleted.
"},{"location":"api/midi/#linuxpy.midi.device.Port.send","title":"send(event_type: Union[str, int, EventType], **kwargs)
","text":"Send a message of the given type from to the destination address(es). Use kwargs to pass specific event arguments like velocity in a \"note on\" event.
event_type can be an instance of EventType or the equivalent number or a case insensitive string matching the event type (ex: \"noteon\", \"NOTEON\", \"note-on\" or \"note on\").
The following example sends \"note on\" on note 42, with velocity 45:
port.send(\"note on\", note=42, velocity=45)\n
"},{"location":"api/midi/#linuxpy.midi.device.Event","title":"Event(event: snd_seq_event)
","text":"Event message object result of listening on a sequencer
"},{"location":"api/midi/#linuxpy.midi.device.Event.__bytes__","title":"__bytes__()
","text":"Serialize the Event in a bytes ready to be sent
"},{"location":"api/midi/#linuxpy.midi.device.Event.new","title":"new(etype: EventT, **kwargs)
classmethod
","text":"Create new Event of the given type
"},{"location":"api/midi/#linuxpy.midi.device.to_address","title":"to_address(addr: FullPortAddress) -> snd_seq_addr
","text":"Convert to low level snd_seq_addr
"},{"location":"api/midi/#linuxpy.midi.device.event_stream","title":"event_stream(sequencer: Sequencer) -> Iterable[Event]
","text":"Infinite stream of events coming from the given sequencer
"},{"location":"api/midi/#linuxpy.midi.device.async_event_stream","title":"async_event_stream(sequencer: Sequencer, maxsize: int = 10) -> AsyncIterable[Event]
async
","text":"Infinite async stream of events coming from the given sequencer
"},{"location":"api/thermal/","title":"Thermal API","text":""},{"location":"api/thermal/#linuxpy.thermal","title":"linuxpy.thermal
","text":"Human friendly interface to linux thermal subsystem.
The heart of linuxpy thermal library are the ThermalZone
and CoolingDevice
classes.
Probably the most common way to create a thermal device is through the find
helper:
with find(type=\"x86_pkg_temp\") as tz:\n print(f\"X86 temperature: {tz.temperature/1000:6.2f} C\")\n
"},{"location":"api/thermal/#linuxpy.thermal.ThermalZone","title":"ThermalZone(syspath: os.PathLike)
","text":" Bases: Device
Thermal sensor
ATTRIBUTE DESCRIPTION type
thermal zone type
TYPE: str
policy
the policy
TYPE: str
available_policies
list of available policies
TYPE: list[str]
temperature
current temperature in milli-celsius
TYPE: int
offset
offet in milli-celsius
TYPE: int
mode
current mode (enabled/disabled)
TYPE: Mode
device_number
thermal device number
TYPE: int
trip_points
list of trip points (new list every time)
TYPE: list[TripPoint]
"},{"location":"api/thermal/#linuxpy.thermal.TripPoint","title":"TripPoint(temperature_path, type_path)
","text":"Trip point associated with the thermal zone
ATTRIBUTE DESCRIPTION temperature
trip point temperature in milli-celsius
TYPE: int
type
trip point type
TYPE: str
"},{"location":"api/thermal/#linuxpy.thermal.CoolingDevice","title":"CoolingDevice(syspath: os.PathLike)
","text":" Bases: Device
Cooling device (fan, processor, ...)
ATTRIBUTE DESCRIPTION type
thermal zone type
TYPE: str
"},{"location":"api/thermal/#linuxpy.thermal.iter_thermal_zone_paths","title":"iter_thermal_zone_paths() -> Iterable[pathlib.Path]
","text":"Returns an iterator over all thermal zone paths
"},{"location":"api/thermal/#linuxpy.thermal.iter_thermal_zone_devices","title":"iter_thermal_zone_devices() -> Iterable[ThermalZone]
","text":"Returns an iterator over all thermal zone devices
"},{"location":"api/thermal/#linuxpy.thermal.iter_cooling_device_paths","title":"iter_cooling_device_paths() -> Iterable[pathlib.Path]
","text":"Returns an iterator over all cooling device paths
"},{"location":"api/thermal/#linuxpy.thermal.iter_cooling_devices","title":"iter_cooling_devices() -> Iterable[CoolingDevice]
","text":"Returns an iterator over all cooling devices
"},{"location":"api/thermal/#linuxpy.thermal.iter_devices","title":"iter_devices() -> Iterable[Union[ThermalZone, CoolingDevice]]
","text":"Returns an iterator over all thermal and cooling devices
"},{"location":"api/thermal/#linuxpy.thermal.find","title":"find(find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs) -> Union[Device, Iterable[Device], None]
","text":"If find_all is False:
Find a device follwing the criteria matched by custom_match and kwargs. If no device is found matching the criteria it returns None. Default is to return a random first device.
If find_all is True:
The result is an iterator. Find all devices that match the criteria custom_match and kwargs. If no device is found matching the criteria it returns an empty iterator. Default is to return an iterator over all input devices found on the system.
"},{"location":"api/video/","title":"Video API","text":""},{"location":"api/video/#linuxpy.video.device","title":"linuxpy.video.device
","text":"Human friendly interface to V4L2 (Video 4 Linux 2) subsystem.
"},{"location":"api/video/#linuxpy.video.device.V4L2Error","title":"V4L2Error
","text":" Bases: Exception
Video for linux 2 error
"},{"location":"api/video/#linuxpy.video.device.Frame","title":"Frame(data: bytes, buff: raw.v4l2_buffer, format: Format)
","text":"The resulting object from an acquisition.
"},{"location":"api/video/#linuxpy.video.device.EventReader","title":"EventReader(device: Device, max_queue_size=100)
","text":""},{"location":"api/video/#linuxpy.video.device.EventReader.aread","title":"aread()
async
","text":"Wait for next event or return last event in queue
"},{"location":"api/video/#linuxpy.video.device.FrameReader","title":"FrameReader(device: Device, raw_read: Callable[[], Buffer], max_queue_size: int = 1)
","text":""},{"location":"api/video/#linuxpy.video.device.FrameReader.aread","title":"aread() -> Frame
async
","text":"Wait for next frame or return last frame
"},{"location":"api/video/#linuxpy.video.device.create_buffer","title":"create_buffer(fd, buffer_type: BufferType, memory: Memory) -> raw.v4l2_buffer
","text":"request + query buffers
"},{"location":"api/video/#linuxpy.video.device.create_buffers","title":"create_buffers(fd, buffer_type: BufferType, memory: Memory, count: int) -> list[raw.v4l2_buffer]
","text":"request + query buffers
"},{"location":"api/video/#linuxpy.video.device.create_mmap_buffers","title":"create_mmap_buffers(fd, buffer_type: BufferType, memory: Memory, count: int) -> list[mmap.mmap]
","text":"create buffers + mmap_from_buffer
"},{"location":"api/video/#linuxpy.video.device.iter_video_files","title":"iter_video_files(path: PathLike = '/dev') -> Iterable[Path]
","text":"Returns an iterator over all video files
"},{"location":"api/video/#linuxpy.video.device.iter_devices","title":"iter_devices(path: PathLike = '/dev', **kwargs) -> Iterable[Device]
","text":"Returns an iterator over all video devices
"},{"location":"api/video/#linuxpy.video.device.iter_video_capture_files","title":"iter_video_capture_files(path: PathLike = '/dev') -> Iterable[Path]
","text":"Returns an iterator over all video files that have CAPTURE capability
"},{"location":"api/video/#linuxpy.video.device.iter_video_capture_devices","title":"iter_video_capture_devices(path: PathLike = '/dev', **kwargs) -> Iterable[Device]
","text":"Returns an iterator over all video devices that have CAPTURE capability
"},{"location":"api/video/#linuxpy.video.device.iter_video_output_files","title":"iter_video_output_files(path: PathLike = '/dev') -> Iterable[Path]
","text":"Some drivers (ex: v4l2loopback) don't report being output capable so that apps like zoom recognize them as valid capture devices so some results might be missing
"},{"location":"api/video/#linuxpy.video.device.iter_video_output_devices","title":"iter_video_output_devices(path: PathLike = '/dev', **kwargs) -> Iterable[Device]
","text":"Returns an iterator over all video devices that have VIDEO OUTPUT capability
"},{"location":"user_guide/","title":"User guide","text":"This tutorial shows you how to use LinuxPy with most of its features.
"},{"location":"user_guide/input/","title":"Input","text":"Human friendly interface to the Linux Input subsystem.
API not documented yet. Just this example:
import time\nfrom linuxpy.input.device import find_gamepads\n\npad = next(find_gamepads())\nabs = pad.absolute\n\nwith pad:\n while True:\n print(f\"X:{abs.x:>3} | Y:{abs.y:>3} | RX:{abs.rx:>3} | RY:{abs.ry:>3}\", end=\"\\r\", flush=True)\n time.sleep(0.1)\n
"},{"location":"user_guide/input/#asyncio","title":"asyncio","text":"python -m asyncio from linuxpy.input.device import find_gamepad with find_gamepad() 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) ..."},{"location":"user_guide/input/#references","title":"References","text":""},{"location":"user_guide/midi/","title":"MIDI Sequencer","text":"Without further ado:
python from linuxpy.midi.device import Sequencer with Sequencer() as 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=0 14:0 Clock queue=0, pad=b'' 14:0 System exclusive F0 61 62 63 F7 14:0 Note off channel=0, note=55, velocity=3, off_velocity=0, duration=0"},{"location":"user_guide/midi/#system-information","title":"System information","text":"$ python\n>>> from linuxpy.midi.device import Sequencer\n>>> seq = Sequencer(\"a midi client\")\n>>> seq.open()\n\n>>> seq.version\n1.0.2\n\n>>> seq.client_info\nsnd_seq_client_info(client=128, type=1, name=b'a midi client', filter=0, multicast_filter=b'', event_filter=b'', num_ports=0, event_lost=0, card=-1, pid=1288570)\n\n>>> seq.running_mode\nsnd_seq_running_info(client=0, big_endian=0, cpu_mode=0, pad=0)\n\n>>> seq.system_info\nsnd_seq_system_info(queues=32, clients=192, ports=254, channels=256, cur_clients=3, cur_queues=0)\n
"},{"location":"user_guide/midi/#asyncio","title":"asyncio","text":"asyncio is a first class citizen to linuxpy.midi:
$ python -m asyncio\n\n>>> from linuxpy.midi.device import Sequencer\n>>> with Sequencer() as seq:\n... port = seq.create_port()\n... port.connect_from(14, 0)\n... async for event in seq:\n... print(event)\n 14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0\n 14:0 Clock queue=0, pad=b''\n 14:0 System exclusive F0 61 62 63 F7\n 14:0 Note off channel=0, note=55, velocity=3, off_velocity=0, duration=0\n
"},{"location":"user_guide/midi/#cli","title":"CLI","text":"A basic CLI is provided that allows listing MIDI clients & ports and dumping MIDI sequencer events:
List all ports:
python -m linuxpy.midi.cli ls Port Client Port Type Capabilities 0:0 System Timer 0 SR, W, R 0:1 System Announce 0 SR, R 14:0 Midi Through Midi Through Port-0 PORT, SOFTWARE, MIDI_GENERIC SW, SR, W, R Listen to events on selected port(s):
python -m linuxpy.midi.cli listen 0:1 14:0 0:1 Port subscribed sender=(client=0, port=1), dest=(client=128, port=0) 0:1 Port start client=128, port=1 0:1 Port subscribed sender=(client=14, port=0), dest=(client=128, port=1) 0:1 Client start client=130, port=0 0:1 Port start client=130, port=0 0: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=0 0:1 Port unsubscribed sender=(client=130, port=0), dest=(client=14, port=0) 0:1 Port exit client=130, port=0 0:1 Client exit client=130, port=0 0:1 Port exit client=129, port=0 0:1 Client exit client=129, port=0 0:1 Client start client=129, port=0 0:1 Port start client=129, port=0 14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0 14:0 Note on channel=0, note=0, velocity=255, off_velocity=0, duration=0 14:0 Note on channel=0, note=0, velocity=255, off_velocity=0, duration=0"},{"location":"user_guide/video/","title":"Video","text":"Human friendly interface to the Video for Linux 2 (V4L2) subsystem.
Without further ado:
python from linuxpy.video.device import Device with Device.from_id(0) as cam: for i, frame in enumerate(cam): print(f\"frame #{i}: {len(frame)} bytes\") 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 ..."},{"location":"user_guide/video/#device-creation","title":"Device creation","text":"Create a device object from an ID:
from linuxpy.video.device import Device\ncamera = Device.from_id(10)\n
from a filename:
from linuxpy.video.device import Device\ncamera = Device(\"/dev/video10\")\n
or from an existing file object:
from linuxpy.video.device import Device\nwith open(\"/dev/video10\", \"rb+\", buffering=0) as fd:\n camera = Device(fd)\n
Before using video Device
object you need to open it (except in the example directly above when creating a device from a file object). You can either use the device object as a context manager (prefered):
with Device.from_id(10) as camera:\n ...\n
The Device object is a reusable, reentrant but not thread safe context manager. This means that Device object can not only be used in multiple with statements, but may also be used inside a with statement that is already using the same context manager.
So the following examples will work just fine:
with Device.from_id(10) as camera:\n ...\n with camera:\n ...\n\nwith camera:\n ...\n
Alternatively, you can manage calls Device.open()
/Device.close()
manually:
camera = Device.from_id(10)\ncamera.open()\ntry:\n ...\nfinally:\n camera.close()\n
"},{"location":"user_guide/video/#capture","title":"Capture","text":"Simple capture without any configuration is possible using the Device object as an infinite iterator:
from linuxpy.video.device import Device, VideoCapture\n\nwith Device.from_id(0) as camera:\n for frame in camera:\n ...\n
The resulting Frame
objects can safely and efficiently be converted to bytes.
To be able to configure the acquisition, you will need to use the VideoCapture
helper. Here is an example with image size and format configuration:
from linuxpy.video.device import Device, VideoCapture\n\nwith Device.from_id(0) as camera:\n capture = VideoCapture(camera)\n capture.set_format(640, 480, \"MJPG\")\n with capture:\n for frame in capture:\n ...\n
Note that VideoCapture
configuration must be done before the capture is started (ie, the with capture:
statement.)
By default, VideoCapture will use memory map if the device has STREAMING capability and falls back to standard read if not. It is also possible to force a specific reader:
from linuxpy.video.device import Capability, Device, VideoCapture\n\nwith Device.from_id(0) as cam:\n with VideoCapture(cam, source=Capability.READWRITE):\n for frame in capture:\n ...\n
"},{"location":"user_guide/video/#information","title":"Information","text":"Getting information about the device:
>>> from linuxpy.video.device import Device, BufferType\n\n>>> cam = Device.from_id(0)\n>>> cam.open()\n>>> cam.info.card\n'Integrated_Webcam_HD: Integrate'\n\n>>> cam.info.capabilities\n<Capability.STREAMING|EXT_PIX_FORMAT|VIDEO_CAPTURE: 69206017>\n\n>>> cam.info.formats\n[ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'Motion-JPEG',\n flags=<ImageFormatFlag.COMPRESSED: 1>, pixelformat=<PixelFormat.MJPEG: 1196444237>),\n ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'YUYV 4:2:2',\n flags=<ImageFormatFlag.0: 0>, pixelformat=<PixelFormat.YUYV: 1448695129>)]\n\n>>> cam.get_format(BufferType.VIDEO_CAPTURE)\nFormat(width=640, height=480, pixelformat=<PixelFormat.MJPEG: 1196444237>}\n\n>>> for ctrl in cam.controls.values(): print(ctrl)\n<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>\n<IntegerControl contrast min=0 max=255 step=1 default=32 value=32>\n<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>\n<IntegerControl hue min=-180 max=180 step=1 default=0 value=0>\n<BooleanControl white_balance_automatic default=True value=True>\n<IntegerControl gamma min=90 max=150 step=1 default=120 value=120>\n<MenuControl power_line_frequency default=1 value=1>\n<IntegerControl white_balance_temperature min=2800 max=6500 step=1 default=4000 value=4000 flags=inactive>\n<IntegerControl sharpness min=0 max=7 step=1 default=2 value=2>\n<IntegerControl backlight_compensation min=0 max=2 step=1 default=1 value=1>\n<MenuControl auto_exposure default=3 value=3>\n<IntegerControl exposure_time_absolute min=4 max=1250 step=1 default=156 value=156 flags=inactive>\n<BooleanControl exposure_dynamic_framerate default=False value=False>\n\n>>> cam.controls[\"saturation\"]\n<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>\n\n>>> cam.controls[\"saturation\"].id\n9963778\n>>> cam.controls[9963778]\n<IntegerControl saturation min=0 max=100 step=1 default=64 value=64>\n\n>>> cam.controls.brightness\n<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>\n>>> cam.controls.brightness.value = 64\n>>> cam.controls.brightness\n<IntegerControl brightness min=0 max=255 step=1 default=128 value=64>\n
(see also v4l2py-ctl example)
"},{"location":"user_guide/video/#asyncio","title":"asyncio","text":"linuxpy.video is asyncio friendly:
python -m asyncio from linuxpy.video.device import Device with Device.from_id(0) as cam: async for frame in cam: print(f\"frame {len(frame)}\") frame 10224 frame 10304 frame 10136 ... (check basic async and web async examples)
"},{"location":"user_guide/video/#gevent","title":"gevent","text":"linuxpy.video is also gevent friendly:
>>> from linuxpy.io import GeventIO\n>>> from linuxpy.video.device import Device\n>>> 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
(check basic gevent and web gevent examples)
"},{"location":"user_guide/video/#video-output","title":"Video output","text":"It 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\n>>> dev_source = Device.from_id(0)\n>>> dev_sink = Device.from_id(10)\n>>> with dev_source, dev_target:\n>>> source = VideoCapture(dev_source)\n>>> sink = VideoOutput(dev_sink)\n>>> source.set_format(640, 480, \"MJPG\")\n>>> sink.set_format(640, 480, \"MJPG\")\n>>> with source, sink:\n>>> for frame in source:\n>>> sink.write(frame.data)\n
By default, VideoOutput will use memory map if the device has STREAMING capability and falls back to standard write if not. It is also possible to force a specific writer with VideoOutput(cam, sink=Capability.READWRITE)
:
"},{"location":"user_guide/video/#v4l2loopback","title":"v4l2loopback","text":"This is just an example on how to setup v4l2loopback.
Start from scratch:
# Remove kernel module and all devices (no client can be connected at this point)\nsudo modprobe -r v4l2loopback\n\n# Install some devices\nsudo modprobe v4l2loopback video_nr=20,21 card_label=\"Loopback 0\",\"Loopback 1\"\n
"},{"location":"user_guide/video/#references","title":"References","text":"See the linux/videodev2.h
header file for details.
V4L2 (Latest) (videodev.h) V4L2 6.2 (videodev.h) "}]}
\ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
index 89dba85..822f209 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -2,52 +2,57 @@
https://tiagocoutinho.github.io/linuxpy/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/develop/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/api/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/api/input/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/api/midi/
- 2023-12-16
+ 2023-12-20
+ daily
+
+
+ https://tiagocoutinho.github.io/linuxpy/api/thermal/
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/api/video/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/user_guide/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/user_guide/input/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/user_guide/midi/
- 2023-12-16
+ 2023-12-20
daily
https://tiagocoutinho.github.io/linuxpy/user_guide/video/
- 2023-12-16
+ 2023-12-20
daily
\ No newline at end of file
diff --git a/sitemap.xml.gz b/sitemap.xml.gz
index 8b63e28..35f4038 100644
Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ
diff --git a/user_guide/index.html b/user_guide/index.html
index fa0663b..5cf71ca 100644
--- a/user_guide/index.html
+++ b/user_guide/index.html
@@ -475,6 +475,8 @@
+
+
@@ -566,6 +568,26 @@
+
+
+
+
+
+ Thermal API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/user_guide/input/index.html b/user_guide/input/index.html
index c51e9ce..d852837 100644
--- a/user_guide/input/index.html
+++ b/user_guide/input/index.html
@@ -529,6 +529,8 @@
+
+
@@ -620,6 +622,26 @@
+
+
+
+
+
+ Thermal API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/user_guide/midi/index.html b/user_guide/midi/index.html
index 198b848..97c5d05 100644
--- a/user_guide/midi/index.html
+++ b/user_guide/midi/index.html
@@ -536,6 +536,8 @@
+
+
@@ -627,6 +629,26 @@
+
+
+
+
+
+ Thermal API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/user_guide/video/index.html b/user_guide/video/index.html
index ff5f35e..c6594f4 100644
--- a/user_guide/video/index.html
+++ b/user_guide/video/index.html
@@ -571,6 +571,8 @@
+
+
@@ -662,6 +664,26 @@
+
+
+
+
+
+ Thermal API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/user_guide/video_demo.svg b/user_guide/video_demo.svg
deleted file mode 100644
index 79754d2..0000000
--- a/user_guide/video_demo.svg
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- coutinho@tiago-crunch : ~/workspace/coutinho/python-linux $ coutinho@tiago-crunch : ~/workspace/coutinho/python-linux $ e coutinho@tiago-crunch : ~/workspace/coutinho/python-linux $ ex coutinho@tiago-crunch : ~/workspace/coutinho/python-linux $ exi coutinho@tiago-crunch : ~/workspace/coutinho/python-linux $ exit coutinho@tiago-crunch : ~/workspace/coutinho/python-linux $ exit exit
-
\ No newline at end of file