Occasional crash during query_caps() when querying v4l2src

Hi,

I’m trying to query the capabilities of the v4l2src to detect what kind of sources are available from the currently connected camera. The approach I’m taking is:

  1. Create the v4l2src with the correct device,
  2. Set the state to PAUSED and wait on get_state() to make sure that it is set correctly,
  3. Iterate through all pads available in v4l2src and on each pad perform query_caps(), and then operate on caps that were retrieved.

The minimal code example I have created is as follows:

#!/usr/bin/env python3

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst

VIDEO_DEV = "/dev/video4"

Gst.init(None)
v4l2 = Gst.parse_launch(f"v4l2src device={VIDEO_DEV}")
v4l2.set_state(Gst.State.PAUSED)
v4l2.get_state(5)
for pad in v4l2.srcpads:
    caps = pad.query_caps(None)
v4l2.set_state(Gst.State.NULL)
v4l2.get_state(5)

But running it in a bash loop shows that it occasionally (once in 10-20 executions) crashes. The backtrace as seen in the core dump is as follows:

#0  0x00007f75e30fe547 in gst_v4l2_object_probe_caps (v4l2object=<optimized out>, filter=<optimized out>) at ../sys/v4l2/gstv4l2object.c:4753
#1  0x00007f75e310c968 in gst_v4l2_object_get_caps (filter=0x0, v4l2object=<optimized out>) at ../sys/v4l2/gstv4l2object.c:4811
#2  gst_v4l2src_get_caps (src=<optimized out>, filter=0x0) at ../sys/v4l2/gstv4l2src.c:870
#3  0x00007f75e30ae852 in gst_base_src_default_query (src=0x563d3f6318a0, query=0x7f75d4000e60) at ../libs/gst/base/gstbasesrc.c:1354
#4  0x00007f75e33864cd in gst_pad_query (pad=0x563d3f8c2930, query=0x7f75d4000e60) at ../gst/gstpad.c:4228
#5  0x00007f75e33cf1ce in gst_pad_query_caps (pad=0x563d3f8c2930, filter=filter@entry=0x0) at ../gst/gstutils.c:3117
#6  0x00007f75e310b5e3 in gst_v4l2src_negotiate (basesrc=0x563d3f6318a0) at ../sys/v4l2/gstv4l2src.c:781
#7  0x00007f75e30a3559 in gst_base_src_negotiate_unlocked (basesrc=basesrc@entry=0x563d3f6318a0) at ../libs/gst/base/gstbasesrc.c:3449
#8  0x00007f75e30a3db0 in gst_base_src_loop (pad=0x563d3f8c2930) at ../libs/gst/base/gstbasesrc.c:2874
#9  0x00007f75e33b6ed4 in gst_task_func (task=0x563d3f8ca420) at ../gst/gsttask.c:384
#10 0x00007f75e42efe02 in g_thread_pool_thread_proxy (data=<optimized out>) at ../glib/gthreadpool.c:350
#11 0x00007f75e42eb573 in g_thread_proxy (data=0x7f75dc000b90) at ../glib/gthread.c:831
#12 0x00007f75f1eac897 in start_thread (arg=<optimized out>) at pthread_create.c:444
#13 0x00007f75f1f33a5c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

dmesg doesn’t show anything suspicious during the crash, and I’ve tried it on two different v4l2 cameras, so I assume that it’s not related to hardware. I’m runnig up-to-date Fedora 39, with GStreamer versions gstreamer1-1.22.12-1.fc39, python-gstreamer1-1.22.12-1.fc39, gstreamer1-plugins-good-1.22.12-1.fc39 and kernel 6.8.10-200.fc39.x86_64.

Is my approach wrong? Thanks in advance for help.

I don’t think that gstreamer is the way to do that (furthermore your code is confusing the pipeline returned by gst_parse_launch and the v4l2src element, so this may explain the crash).

What you want is available from V4L API. Try:

v4l2-ctl --device=/dev/video4 --list-formats-ext

Note that v4l2-ctl command is provided by package v4l-utils.

If this fits your needs, you may launch the command in subprocess and parse its output, or if you want to send ioctls to V4L, v4l2-ctl open source code can be found online.

You may also try:

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst

Gst.init(None)
pipeline = Gst.parse_launch ('v4l2src device=/dev/video4 name=camera ! fakesink')
camera = pipeline.get_by_name("camera");
pipeline.set_state(Gst.State.READY)
pipeline.get_state(5)
# Note that v4l2src may only have one SRC pad named 'src'
pad = camera.get_static_pad("src")
caps = pad.query_caps(None)
if caps != None:
    print("%s" % caps.to_string())
pipeline.set_state(Gst.State.NULL)

Note that this would show negociated caps, though if no camera is connected to V4L node, then all possible video caps may be returned, that is not what you would want. V4L API may be more reliable.

1 Like

I know about querying v4l2 directly, and I wanted to avoid it if possible - I’d prefer to do as much as possible through GStreamer, so if I’d ever need to switch the video source to something else I could hope for the rest of the code to stay as it is.

Also I find GStreamer’s API way more convenient than ioctl() :slightly_smiling_face:

The second approach seems useful for me to get the negotiated caps when the streaming will be already ongoing - so thanks for that, I will definitely use it.

I’ve also took a closer look at my previous solution, compared it with your example, and tried two things:

  • Setting v4l2src to the READY state (crashes stopped),
  • and getting static pad instead of iterating through all of them (no difference in crashing).

And it looks like changing PAUSED to READY caused the crashes to go away.

Thanks a lot for your help! I will avoid using GStreamer that way if it’s discouraged, but it’s always good to find a solution.

The non-crashing version of the original script, if anyone needs it:

#!/usr/bin/env python3

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst

Gst.init(None)
v4l2 = Gst.parse_launch(f"v4l2src device={VIDEO_DEV} name=camera")
v4l2.set_state(Gst.State.READY)
v4l2.get_state(5)
pad = v4l2.get_static_pad("src")
caps = pad.query_caps(None)
print(f"Caps: {caps.to_string()}")
v4l2.set_state(Gst.State.NULL)
v4l2.get_state(5)