Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bunch of V4L2 features #9

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Bunch of V4L2 features #9

wants to merge 2 commits into from

Conversation

sylpe
Copy link

@sylpe sylpe commented Dec 11, 2024

  • First commits are basics v4l2 features.
  • Ext Ctrl Support is quite basic
  • Stride issue has been observed on exotic resolutions but may be fixed with new handling of stride in pixutils

v4l2/subdev.py Outdated Show resolved Hide resolved
@sylpe sylpe force-pushed the v4l2 branch 2 times, most recently from 8875a62 to 58dbcc2 Compare December 12, 2024 07:56
v4l2/videodev.py Outdated
@@ -10,7 +10,7 @@

import v4l2.uapi

__all__ = [ 'VideoDevice', 'VideoBuffer' ]
__all__ = [ 'VideoDevice', 'VideoBuffer', 'CaptureStreamer' ]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this? The commit description should explain why the patch is needed, unless it's very obvious.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry, it lacks the background ... In fact I have a WIP 'mediapipe' branch (https://github.com/sylpe/pyv4l2/tree/mediapipe) where I tried to abstract a bit the media pipeline (and for this I use CaptureStreamer class).
So you're right this commit has nothing to do here; I'll move it.

def set_ext_ctrl(self, ctrl_id, ctrl_val):
# TODO: handle situation when control is dynamically grabbed
if self.controls[ctrl_id].flags & v4l2.uapi.V4L2_CTRL_FLAG_READ_ONLY:
print(f"Control id [{ctrl_id}] isn't writable")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should raise an exception.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sure... I realized now the poor quality of this PR...
Let's take it as an opportunity to exchange a bit on some implementation choices:

Regarding ext ctrls, I made the choice to enumerate controls at SubDevice initialization :
self.controls = self.enum_ext_ctrls()
This allow to do additionnal tests (READ_ONLY, GRABBED) in set_ext_ctrl(). However I'm not sure it's in the same philosophy than the rest of the code.
Furthermore it's not fully implemented and doesn't support dynamically grabbed controls (which would requires to enum_ext_ctrls().

Would you mind giving me your opinion and I could propose an updated implementation.

print(f"Control id [{ctrl_id}] isn't writable")
return
if self.controls[ctrl_id].flags & v4l2.uapi.V4L2_CTRL_FLAG_GRABBED:
print(f'Control id [{ctrl_id}] temporarily grabbed')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same than above :-)

ctrl.value64
if (self.controls[ctrl_id].type == v4l2.uapi.V4L2_CTRL_TYPE_INTEGER64)
else ctrl.value
)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never seen this style used before. Maybe it's just me, but that's very unclear to me. How about just:

if self.controls[ctrl_id].type == v4l2.uapi.V4L2_CTRL_TYPE_INTEGER64:
    v = ctrl.value64
else:
    v = ctrl.value

return v

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, its not you :-), just badly written... Will fix it.
It also raise questions about the type of controls to support (In a first step, I only needed INTEGER64).

ext_ctrl.which = v4l2.uapi.V4L2_CTRL_WHICH_CUR_VAL
ext_ctrl.count = 1
ext_ctrl.controls = ctypes.pointer(ctrl)
fcntl.ioctl(self.fd, v4l2.uapi.VIDIOC_S_EXT_CTRLS, ext_ctrl)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would help me to understand what you need if you would describe in the commit desc what you use this for (similarly for exporting CaptureStreamer). Looks like you just use it to set/get a single 32/64 bit value?

I'm not super familiar with v4l2 controls as I haven't used them too much, but I think I'd really like to be able to expose a unified API for the users. No "ext" vs "non-ext" stuff, but just a way to get and set controls, regardless if the driver supports ext or not.

So, we already have the "naive" set_control(), I'd like to see it extended so it just works, instead of adding set_ext_ctrl(), requiring the caller to know which one to use.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, As explained above, I have a WIP 'mediapipe' branch (https://github.com/sylpe/pyv4l2/tree/mediapipe).
On top of this I have some pytests which allows me to tests camera driver : mostly iterating over all the formats (width, heigth, pixelformat), and play with controls.

Please find below an example of how I use it :

@pytest.mark.parametrize('duration', [2, 10], ids=duration_name)
@pytest.mark.parametrize('tp', [VD56G3_TP_HGREY, VD56G3_TP_VGREY], ids=tp_name)
def test_stream_tp(media_conf, duration, tp, request):
    """
    Test that the sensor stream correctly with Grey gradient Test Patterns.

    Ensure the output framerate is correct.
    Ensure the obtained image fit with format width and height.
    Ensure the orientation of the test pattern comply with the one configured.
    Save last frame as an image (for human inspection).
    """
    # Init : reset frame holder
    frames_holder.clear()
    # Init : setup media pipe
    media_pipe = MediaPipe(media_conf)
    media_pipe.setup_links()
    media_pipe.configure_subdevs()
    # Init : reset/set camera subdevice controls
    camera_subdev = media_pipe.subdevices[media_conf.get_src_entity().name]
    reset_camera_subdev_ctrls(camera_subdev)
    camera_subdev.set_ext_ctrl(v4l2.uapi.V4L2_CID_TEST_PATTERN, tp)
    # Init : gather test values
    (width, height, _) = get_subdev_source_fmt(media_conf)
    expected_fps = get_theorical_fps(camera_subdev)

    # Stream for 'duration' secs
    media_pipe.start_stream(on_frame_cb=collect_frame)
    sleep(duration)
    media_pipe.stop_stream()

    # Normalize collected frame in np arrays
    normalize_frames_holder()

    # Analyze output : framerate
    assert duration * expected_fps - 2 <= len(frames_holder) <= duration * expected_fps + 2
    # Analyze output : image format (width, height)
    assert frames_holder[0][2] == width
    assert frames_holder[0][3] == height
    # Analyze output : check images orientation (for first and last frame captured)
    orientation, magnitude = avr_gradient_direction(frames_holder[0][0])
    assert orientation <= 0.01 if tp == VD56G3_TP_HGREY else 89.99 < orientation < 90.01
    orientation, magnitude = avr_gradient_direction(frames_holder[-1][0])
    assert orientation <= 0.01 if tp == VD56G3_TP_HGREY else 89.99 < orientation < 90.01
    # Save last image present in the frame_holder
    test_name = request.node.name[5:]
    save_to_file(test_name, -1)

This could help understand better the context.

I would have like to use the existing set_control(), but for some of the controls (especially integer64) and flags, It was possible to go through the "legacy" control API.

I'm totally aligned with you, better to improve the existing API. Depending of your thoughts I could proposed a better implementation fitting with pyv4l2 philosophy.

@@ -3613,6 +3613,7 @@ class struct_v4l2_ext_control(Structure):
'reserved2',
'unnamed_1',
]
struct_v4l2_ext_control._pack_ = 1
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is generated, you can't modify it as the modification will get overwritten. If this is really needed, then something needs to be done either in the generator script or init.py. Or maybe there's a bug in the ctypesgen, which needs to be fixed.

It's also not clear to me why this is needed or how it helps... struct_v4l2_ext_control has u32 fields, and the last one is an union. How does it get packed without this change? Which field gets extra alignment without this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I didn't really understand why this was needed...
To be honest, I used previously on my side another python library (pyv4l2 alternative) which were working correctly with ext ctrls ... I had to replicate this packing option to make pyv4l2 work correctly...
I would have been happy if you had a start of explanation :-D

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The union_anon_15 is 8 bytes, and ctypes will align that to 8 bytes, which means 4 bytes of padding before the union. This increases the size of the struct from 20 to 24 bytes. The c-header is marked with attribute ((packed)), so this looks like a bug in ctypesgen...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v4l2/videodev.py Outdated Show resolved Hide resolved
v4l2/videodev.py Outdated Show resolved Hide resolved
v4l2/videodev.py Outdated Show resolved Hide resolved
@tomba
Copy link
Owner

tomba commented Dec 12, 2024

I've pushed the first two patches. I'll reply to the rest later.

Sylvain Petinot added 2 commits December 12, 2024 14:48
This is just a basic proposal. Many things are missings :
- doesn't cover all control types
- only implemented in SubDevice while it could probably be generalized
to VideoDevice

Signed-off-by: Sylvain Petinot <[email protected]>
Looks like the v4l2_ext_control struct must be packed with an alignment
of 1 byte.

Signed-off-by: Sylvain Petinot <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants