joshdoe / gst-plugins-vision

GStreamer plugins related to the field of machine vision
Other
134 stars 50 forks source link

How to interrupt pylonsrc grab-timeout when setting pipeline state to NULL? #62

Closed mkaivs closed 3 years ago

mkaivs commented 3 years ago

I have a pipeline that makes use of the pylonsrc plugin, it's in trigger mode with grab-timeout set to 60000 ms (1 min). When user press CTRL+C, the bus will handle it by stopping the mainloop, and subsequently, call gst_element_set_state(pipeline, GST_STATE_NULL);, however, the program will wait for the grab-timeout amount before fully exit. Is there a way interrupt the grab-timeout itself?

mrstecklo commented 3 years ago

Generally you shouldn't need that long grab-timeout. You can try increasing frame-drop-limit. Though both these configurations are too rough and straight-forward.

If you are experiencing grab errors probably the rest of your pipeline is too slow. Try running your pipeline with just pylonsrc, some capsfilter probably and a fakesink. If that doesn't help, you should manage bandwidth configuration. See this. Transport layer features can be set with packet-size, inter-packet-delay, frame-trans-delay, bandwidth-reserve and bandwidth-reserve-acc plugin properties.

If you really want to leave 1 min timeout, then you could try putting PylonWaitObjectSignal(src->waitObject) in whichever function (probably gst_pylonsrc_stop) is called to interrupt a plugin. But I'm almost sure that gst_pylonsrc_stop is called in the main thread as well as gst_pylonsrc_create which is waiting in this case

mkaivs commented 3 years ago

@mrstecklo thank you for the pointer. The timeout is set to wait for objects that will be captured by the camera rather than for the pipeline. I follow your advice and modified the gst_pylonsrc_stop function as follow:

static gboolean
gst_pylonsrc_stop (GstBaseSrc * bsrc)
{
  GstPylonSrc *src = GST_PYLONSRC (bsrc);
  GST_DEBUG_OBJECT (src, "stop");

  // interrupt grab-timeout
  GST_DEBUG_OBJECT (src, "singal wait object");
  PylonWaitObjectSignal(src->waitObject);
  pylonc_disconnect_camera (src);

  return TRUE;
}

But it doesn't work:

^C
Posting UserInterrupt message to bus. 
UserInterrupt message posted, stopping pipeline...
Call g_main_loop_quit(loop);
Returned, stopping playback
Call gst_element_set_state(pipeline, GST_STATE_NULL); 
0:01:03.759489213  9727 0x55da5e5d8400 ERROR               pylonsrc gstpylonsrc.c:4092:gst_pylonsrc_create:<camera> Camera couldn't prepare the buffer in time. Probably dead.
0:01:03.759616837  9727 0x55d9fa5cfc00 DEBUG               pylonsrc gstpylonsrc.c:4161:gst_pylonsrc_stop:<camera> stop
0:01:03.759656304  9727 0x55d9fa5cfc00 DEBUG               pylonsrc gstpylonsrc.c:4164:gst_pylonsrc_stop:<camera> singal wait object
0:01:03.793825385  9727 0x55d9fa5cfc00 DEBUG               pylonsrc gstpylonsrc.c:4219:pylonc_disconnect_camera:<camera> Camera disconnected.
Deleting pipeline
0:01:03.886512759  9727 0x55d9fa5cfc00 DEBUG               pylonsrc gstpylonsrc.c:4175:gst_pylonsrc_dispose:<camera> dispose
0:01:03.886539016  9727 0x55d9fa5cfc00 DEBUG               pylonsrc gstpylonsrc.c:4183:gst_pylonsrc_finalize:<camera> finalize
0:01:03.889187367  9727 0x55d9fa5cfc00 DEBUG               pylonsrc gstpylonsrc.c:4201:gst_pylonsrc_finalize:<camera> Last object finalized
Exit program

gst_pylonsrc_stop is only called after gst_pylonsrc_create timeout. I checked the gst_pylonsrc_create and gst_pylonsrc_stop is not called there.

I have also tried to use PylonWaitObjectWaitEx instead of PylonWaitObjectWait in the gst_pylonsrc_create function but that doesn't work either.

How do I go from here?

mrstecklo commented 3 years ago

@mkaivs I would start from here

mrstecklo commented 3 years ago

The problem is that most functions are called in one thread. If I'm not mistaken this 0x55da5e5d8400 in gstreamer output is a thread ID

mkaivs commented 3 years ago

@mrstecklo

You're correct 0x55da5e5d8400 is the thread ID. The gst_pylonsrc_stop is called from the main thread only after the gst_pylonsrc_create returned. You mentioned that "The problem is that most functions are called in one thread.", I'm not sure what you meant by that?

Regarding your suggestions, I have tried called gst_base_src_set_async from gst_pylonsrc_init as follow:

@@ -882,6 +990,8 @@ gst_pylonsrc_init (GstPylonSrc * src)
   gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
   gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
   gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE);
+
+  gst_base_src_set_async(GST_BASE_SRC (src), TRUE);
 }

I still wasn't able to interrupt the gst_pylonsrc_create function.

I also tried to signal the wait object within the gst_pylonsrc_create by creating a new thread:

gpointer signal_wait_object(gpointer data)
{
  sleep(5);
  GST_DEBUG("Signal wait object %p, %ld, %d", g_thread_self(), syscall(__NR_gettid), getpid());
  PYLON_WAITOBJECT_HANDLE *wait_object = (PYLON_WAITOBJECT_HANDLE *)data;
  GENAPIC_RESULT res = PylonWaitObjectSignal(*wait_object);

  if(res != GENAPI_E_OK)
  {
    GST_DEBUG("Fail to singal wait object");
    char* errMsg; 
    size_t length; 
    GenApiGetLastErrorMessage( NULL, &length ); 
    errMsg = (char*) malloc( length ); 
    GenApiGetLastErrorMessage( errMsg, &length ); 
    GST_DEBUG("PylonC error: %s (%#08x).\n", errMsg, (unsigned int) res); 
    free(errMsg); 
    GenApiGetLastErrorDetail( NULL, &length ); 
    errMsg = (char*) malloc( length ); 
    GenApiGetLastErrorDetail( errMsg, &length ); 
    GST_DEBUG("PylonC error: %s\n", errMsg); 
    free(errMsg);
  }

  return wait_object;

}

static GstFlowReturn
gst_pylonsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
{
  // code not shown

  GThread * interrupt_thread = g_thread_new (
              "interrupt_thread",
              signal_wait_object,
              &src->waitObject);
  g_thread_unref(interrupt_thread);            

  // Wait for the buffer to be filled  (up to n ms). Can fail on large frames if timeout set too low.
  GST_DEBUG_OBJECT (src, "Waiting for triger ... %p, %ld, %d", g_thread_self(), syscall(__NR_gettid), getpid());
  res = PylonWaitObjectWait (src->waitObject, src->grabtimeout, &bufferReady);

  // code not shown
}

The output:

0:00:03.738156463  9151 0x5629ebc0e8a0 DEBUG               pylonsrc gstpylonsrc.c:4236:gst_pylonsrc_create:<camera> Waiting for triger ... 0x5629ebc0e8a0, 9198, 9151   
0:00:08.738424676  9151 0x7f76d400aa30 DEBUG               pylonsrc gstpylonsrc.c:4184:signal_wait_object: Signal wait object 0x7f76d400aa30, 9201, 9151
0:00:08.738673703  9151 0x7f76d400aa30 DEBUG               pylonsrc gstpylonsrc.c:4190:signal_wait_object: Fail to singal wait object
0:00:08.738704197  9151 0x7f76d400aa30 DEBUG               pylonsrc gstpylonsrc.c:4196:signal_wait_object: PylonC error: PylonC error #c2000002 'Failed to signal wait object.' (0xc2000002).

0:00:08.738721511  9151 0x7f76d400aa30 DEBUG               pylonsrc gstpylonsrc.c:4201:signal_wait_object: PylonC error: Function 'PylonWaitObjectSignal' failed in source file '../../../../../../../Pylon/PylonC/PylonC/PylonC.cpp' line #3127
Caused by an exception thrown from source file '../../../../../../../Pylon/PylonC/PylonC/PylonC.cpp' line #3123.
Wait object cannot be signaled

So the wait object cannot be signalled externally?

mrstecklo commented 3 years ago

@mkaivs

You mentioned that "The problem is that most functions are called in one thread.", I'm not sure what you meant by that?

I mean that calling PylonWaitObjectSignal in the same thread as PylonWaitObjectWait is useless. And that is what GStreamer does by default. So I hoped that calling PylonWaitObjectSignal from a new thread would help. Seems like it doesn't. I'm not really familiar with Pylon wait objects.

Probably that is not the point, but I'm confused with your address-off/indirection dance around waitObject. PYLON_WAITOBJECT_HANDLE is a pointer itself. So you can pass it directly to g_thread_new

gpointer signal_wait_object(gpointer data)
{
  ...
  PYLON_WAITOBJECT_HANDLE wait_object = (PYLON_WAITOBJECT_HANDLE) data;
  ...
}

static GstFlowReturn
gst_pylonsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
{
  ...
  GThread * interrupt_thread = g_thread_new (
              "interrupt_thread",
              signal_wait_object,
              src->waitObject);
  ...
}

The timeout is set to wait for objects that will be captured by the camera rather than for the pipeline

I still don't understand what objects is your camera waiting for so long.

mkaivs commented 3 years ago

@mrstecklo,

Thank you for the clarification. I read the pylon doc and found this:

pylon C provides a generalized mechanism for applications to wait for externally generated events, based on the concepts of wait objects and wait object containers. Wait objects provide an abstraction layer for operating system-specific synchronization mechanisms. Events in pylon C include image data that become available at a stream grabber (see Retrieving Grabbed Images), or event data that become available at an event grabber. With wait objects, pylon C provides a mechanism for applications to wait for these events. Moreover, applications can create wait objects of their own that can be explicitly signaled.

So the reason PylonWaitObjectSignal failed is because the wait object of the stream grabber cannot be signalled by the application.