otaku42 / v4l2py

V4L2 python library
GNU General Public License v3.0
0 stars 0 forks source link

Implement V4L2 control types as distinct Python classes #15

Closed otaku42 closed 1 year ago

otaku42 commented 1 year ago

By now there is one generic Control class that is used to represent (presumably) all different V4L2 control types. While this works, it can complicate things when trying to correctly handle certain aspects where control types behave differently - see #12, for example.

One way to address this problem would be to use distinct Python classes to represent the various control types and taking care of such differences within the corresponding class. Inheritance should be used to cover stuff that is similar over various control types.

After digging a bit deeper into the V4L2 controls documentation (see #14), I think the following inheritance model should work:

BaseControl
+-- BaseSingleControl
|   +-- LegacyControl
|   +-- GenericControl
|   +-- BaseNumericControl
|   |   +-- IntegerControl
|   |   +-- Integer64Control
|   |   +-- (U8Control)
|   |   +-- (U16Control)
|   |   +-- (U32Control)
|   +-- BooleanControl
|   +-- (ButtonControl)
+-- BaseMenuControl
|   +-- StringMenuControl
|   +-- IntegerMenuControl
+-- BaseCompoundControl

The LegacyControl class would be the drop-in-replacement for today's Control class, allowing to use s/Control/LegacyControl/ as an easy first-step upgrade path for existing code.

Deriving BaseMenuControl from BaseControl rather than BaseSingleControl is basically the response to #12. I'll have to see if that actually makes sense once implementing this whole idea.

One thing I'm not sure about is whether handling of compound controls is different to that of non-compound controls. The documentation of the V4L2_CTRL_FLAG_HAS_PAYLOAD flag says:

_This control has a pointer type, so its value has to be accessed using one of the pointer fields of struct v4l2_ext_control. This flag is set for controls that are an array, string, or have a compound type. In all cases you have to set a pointer to memory containing the payload of the control._

This leads to my understanding that getting values from compound and non-compound controls work differently. I have no device with a driver that presents any compound control, so I can not verify this further at this point. Assuming that the understanding is correct, I will distinct between BaseSingleControl and BaseCompoundControl, and leave implementation of the latter for later (or to someone else).

otaku42 commented 1 year ago

It turns out that the inheritance model can be simplified a little. Class BaseSingleControl can be dissolved into BaseControl and BaseNumericControl, which allows to get rid of one subclass level:

BaseControl
+-- BaseNumericControl
|   +-- IntegerControl
|   +-- Integer64Control
|   +-- (U8Control)
|   +-- (U16Control)
|   +-- (U32Control)
|   |
|   +-- LegacyControl
|
+-- BaseMenuControl
|   +-- StringMenuControl
|   +-- IntegerMenuControl
+-- GenericControl
+-- BooleanControl
+-- (ButtonControl)
+-- BaseCompoundControl

LegacyControl becoming a subclass of BaseNumericControl may appear weird at first, but it makes sense from the inheritance point-of-view.

otaku42 commented 1 year ago

Next iteration:

BaseControl
+-- BaseNumericControl
|   +-- IntegerControl
|   +-- Integer64Control
|   +-- (U8Control)
|   +-- (U16Control)
|   +-- (U32Control)
|   |
|   +-- LegacyControl
|
+-- MenuControl
+-- GenericControl
+-- BooleanControl
+-- (ButtonControl)
+-- BaseCompoundControl

BaseMenuControl along with the subclasses StringMenuControl and IntegerMenuControl are reduced to MenuControl. I think the distinction between string and integer elements does not need to happen on the control class level, but rather on the MenuItem level.

otaku42 commented 1 year ago

While working on MenuControl, it crossed my mind that this control type would benefit from being dict-like. The initial implementation of that idea then showed that the current approach of representing the items of a menu as MenuItem objects is hindering. In this context the MenuItem class had no benefit either, so I decided to not make use of it in MenuControl. It's kept for backward compatibility only, along with LegacyControl. See 6e0f5f6.

otaku42 commented 1 year ago

As of 654e47d the inheritance model now looks like this:

BaseControl
+-- BaseMonoControl
|   +-- BaseNumericControl
|   |   +-- IntegerControl .... V4L2_CTRL_TYPE_INTEGER
|   |   +-- Integer64Control .. V4L2_CTRL_TYPE_INTEGER64
|   |   +-- U8Control ......... V4L2_CTRL_TYPE_U8
|   |   +-- U16Control ........ V4L2_CTRL_TYPE_U16
|   |   +-- U32Control ........ V4L2_CTRL_TYPE_U32
|   |   |
|   |   +-- LegacyControl
|   |
|   +-- MenuControl ........... V4L2_CTRL_TYPE_(INTEGER_)MENU
|   +-- GenericControl
|   +-- BooleanControl ........ V4L2_CTRL_TYPE_BOOLEAN
+-- ButtonControl ............. V4L2_CTRL_TYPE_BUTTON
+-- (BaseCompoundControl)

tbd:
 * StringControl (= V4L2_CTRL_TYPE_STRING)
 * BitmaskControl (= V4L2_CTRL_TYPE_BITMASK)

StringControl might be special, in that it seems to use a payload as compound controls do. As I don't have a device that implements string or bitmask controls, implementing these two control types is not high on my todo list. This might change once #17 gets fixed, though.