From 26d0ee302195ad248edf7e7e497836164c6f29b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Pacaud Date: Wed, 4 Sep 2024 16:05:29 +0200 Subject: [PATCH] gst: set_trigger & software_trigger added for aravissrc Co-authored-by: WhaSukGO --- gst/gstaravis.c | 74 ++++++++++++++++++--- gst/gstaravis.h | 4 ++ tests/meson.build | 1 + tests/pylaunch-dbg.in | 1 + tests/pylaunch.in | 1 + tests/python/arv-gst-software-trigger.py | 82 ++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 10 deletions(-) create mode 100755 tests/python/arv-gst-software-trigger.py diff --git a/gst/gstaravis.c b/gst/gstaravis.c index d08f82c37..3292d46ab 100644 --- a/gst/gstaravis.c +++ b/gst/gstaravis.c @@ -37,6 +37,7 @@ #include #include #include +#include /* TODO: Add l10n */ #define _(x) (x) @@ -67,11 +68,21 @@ enum PROP_NUM_ARV_BUFFERS, PROP_USB_MODE, PROP_STREAM, + PROP_TRIGGER, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES]; +enum +{ + /* actions */ + SIGNAL_SOFTWARE_TRIGGER, + LAST_SIGNAL +}; + +static guint gst_aravis_signals[LAST_SIGNAL] = { 0 }; + #define GST_TYPE_ARV_AUTO (gst_arv_auto_get_type()) static GType gst_arv_auto_get_type (void) @@ -133,7 +144,7 @@ gst_aravis_get_all_camera_caps (GstAravis *gst_aravis, GError **error) int min_frame_rate_denominator; int max_frame_rate_numerator; int max_frame_rate_denominator; - gboolean is_frame_rate_available; + gboolean use_frame_rate; g_return_val_if_fail (GST_IS_ARAVIS (gst_aravis), NULL); @@ -146,8 +157,10 @@ gst_aravis_get_all_camera_caps (GstAravis *gst_aravis, GError **error) if (!local_error) arv_camera_get_height_bounds (gst_aravis->camera, &min_height, &max_height, &local_error); if (!local_error) pixel_formats = arv_camera_dup_available_pixel_formats (gst_aravis->camera, &n_pixel_formats, &local_error); - is_frame_rate_available = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL); - if (is_frame_rate_available) { + use_frame_rate = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL) && + gst_aravis->trigger_source == NULL; + + if (use_frame_rate) { if (!local_error) arv_camera_get_frame_rate_bounds (gst_aravis->camera, &min_frame_rate, &max_frame_rate, &local_error); if (!local_error) { @@ -174,7 +187,7 @@ gst_aravis_get_all_camera_caps (GstAravis *gst_aravis, GError **error) "width", GST_TYPE_INT_RANGE, min_width, max_width, "height", GST_TYPE_INT_RANGE, min_height, max_height, NULL); - if (is_frame_rate_available) + if (use_frame_rate) gst_structure_set (structure, "framerate", GST_TYPE_FRACTION_RANGE, min_frame_rate_numerator, min_frame_rate_denominator, @@ -395,6 +408,13 @@ gst_aravis_set_caps (GstBaseSrc *src, GstCaps *caps) return result; } +static void +gst_aravis_software_trigger (GstAravis *src) { + if (ARV_IS_CAMERA (src->camera)) { + arv_camera_software_trigger(src->camera, NULL); + } +} + static gboolean gst_aravis_init_camera (GstAravis *gst_aravis, gboolean *notify, GError **error) { @@ -415,6 +435,8 @@ gst_aravis_init_camera (GstAravis *gst_aravis, gboolean *notify, GError **error) if (!local_error) gst_aravis->payload = 0; if (!local_error && arv_camera_is_uv_device (gst_aravis->camera)) arv_camera_uv_set_usb_mode (gst_aravis->camera, gst_aravis->usb_mode); + if (!local_error && gst_aravis->trigger_source != NULL) + arv_camera_set_trigger (gst_aravis->camera, gst_aravis->trigger_source, &local_error); if (local_error) { g_clear_object (&gst_aravis->camera); @@ -615,15 +637,19 @@ gst_aravis_fixate_caps (GstBaseSrc * bsrc, GstCaps * caps) gint width; gint height; double frame_rate = 0.0; - gboolean is_frame_rate_available; + gboolean use_frame_rate; g_return_val_if_fail (GST_IS_ARAVIS (bsrc), NULL); GST_OBJECT_LOCK (gst_aravis); + arv_camera_get_region (gst_aravis->camera, NULL, NULL, &width, &height, &error); - is_frame_rate_available = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL); - if (is_frame_rate_available) + use_frame_rate = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL) && + gst_aravis->trigger_source == NULL; + + if (use_frame_rate) if (!error) frame_rate = arv_camera_get_frame_rate (gst_aravis->camera, &error); + GST_OBJECT_UNLOCK (gst_aravis); if (error) { GST_ELEMENT_ERROR (gst_aravis, RESOURCE, READ, @@ -637,7 +663,7 @@ gst_aravis_fixate_caps (GstBaseSrc * bsrc, GstCaps * caps) gst_structure_fixate_field_nearest_int (structure, "width", width); gst_structure_fixate_field_nearest_int (structure, "height", height); - if (is_frame_rate_available) + if (use_frame_rate) gst_structure_fixate_field_nearest_fraction (structure, "framerate", (double) (0.5 + frame_rate), 1); @@ -676,6 +702,8 @@ gst_aravis_init (GstAravis *gst_aravis) gst_aravis->buffer_timeout_us = GST_ARAVIS_BUFFER_TIMEOUT_DEFAULT; gst_aravis->frame_rate = 0.0; + gst_aravis->trigger_source = NULL; + gst_aravis->camera = NULL; gst_aravis->stream = NULL; @@ -807,6 +835,12 @@ gst_aravis_set_property (GObject * object, guint prop_id, case PROP_USB_MODE: gst_aravis->usb_mode = g_value_get_enum (value); break; + case PROP_TRIGGER: + GST_OBJECT_LOCK (gst_aravis); + g_free (gst_aravis->trigger_source); + gst_aravis->trigger_source = g_strdup (g_value_get_string (value)); + GST_OBJECT_UNLOCK (gst_aravis); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -909,6 +943,11 @@ gst_aravis_get_property (GObject * object, guint prop_id, GValue * value, GST_OBJECT_LOCK (gst_aravis); g_value_set_object (value, gst_aravis->stream); GST_OBJECT_UNLOCK (gst_aravis); + break; + case PROP_TRIGGER: + GST_OBJECT_LOCK (gst_aravis); + g_value_set_string (value, gst_aravis->trigger_source); + GST_OBJECT_UNLOCK (gst_aravis); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -937,7 +976,7 @@ gst_aravis_query (GstBaseSrc *bsrc, GstQuery *query) } /* we must have a framerate */ - if (src->frame_rate <= 0.0) + if (src->frame_rate <= 0.0 || src->trigger_source != NULL) { GST_WARNING_OBJECT (src, "Can't give latency since framerate isn't fixated !"); goto done; @@ -1092,12 +1131,25 @@ gst_aravis_class_init (GstAravisClass * klass) "Stream instance to retrieve additional information", ARV_TYPE_STREAM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + properties[PROP_TRIGGER] = + g_param_spec_string("trigger", + "Configure the trigger mode", + "Enable the trigger mode using the given source", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, G_N_ELEMENTS (properties), properties); - GST_DEBUG_CATEGORY_INIT (aravis_debug, "aravissrc", 0, "Aravis interface"); + gst_aravis_signals[SIGNAL_SOFTWARE_TRIGGER] = + g_signal_new ("software-trigger", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GstAravisClass, + software_trigger), NULL, NULL, NULL, + G_TYPE_NONE, 0); + + GST_DEBUG_CATEGORY_INIT (aravis_debug, "aravissrc", 0, "Aravis interface"); gst_element_class_set_details_simple (element_class, "Aravis Video Source", @@ -1117,6 +1169,8 @@ gst_aravis_class_init (GstAravisClass * klass) gstbasesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_aravis_get_times); gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_aravis_create); + + klass->software_trigger = gst_aravis_software_trigger; } static gboolean diff --git a/gst/gstaravis.h b/gst/gstaravis.h index 6778f62e3..fb5f14476 100644 --- a/gst/gstaravis.h +++ b/gst/gstaravis.h @@ -78,11 +78,15 @@ struct _GstAravis { guint64 timestamp_offset; guint64 last_timestamp; + char *trigger_source; + char *features; }; struct _GstAravisClass { GstPushSrcClass parent_class; + + void (*software_trigger) (GstAravis *src); }; GType gst_aravis_get_type (void); diff --git a/tests/meson.build b/tests/meson.build index 223334179..2c5df5802 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -42,6 +42,7 @@ if get_option('tests') py_script_config_data = configuration_data () py_script_config_data.set ('GI_TYPELIB_PATH', meson.project_build_root() / 'src') py_script_config_data.set ('LD_LIBRARY_PATH', meson.project_build_root() / 'src') + py_script_config_data.set ('GST_PLUGIN_PATH', meson.project_build_root() / 'gst') py_script_config_data.set ('FAKE_GENICAM_PATH', meson.project_source_root() / 'src' / 'arv-fake-camera.xml') if get_option('gentl-producer') py_script_config_data.set ('ARV_PRODUCER_PATH', gentl_producer_library.full_path()) diff --git a/tests/pylaunch-dbg.in b/tests/pylaunch-dbg.in index 2d0250bde..9a2aab7e0 100755 --- a/tests/pylaunch-dbg.in +++ b/tests/pylaunch-dbg.in @@ -2,6 +2,7 @@ export GI_TYPELIB_PATH=@GI_TYPELIB_PATH@ export LD_LIBRARY_PATH=@LD_LIBRARY_PATH@ +export GST_PLUGIN_PATH=@GST_PLUGIN_PATH@ export FAKE_GENICAM_PATH=@FAKE_GENICAM_PATH@ export ARV_PRODUCER_PATH=@ARV_PRODUCER_PATH@ diff --git a/tests/pylaunch.in b/tests/pylaunch.in index 5a7c19caa..8c150e4e1 100755 --- a/tests/pylaunch.in +++ b/tests/pylaunch.in @@ -2,6 +2,7 @@ export GI_TYPELIB_PATH=@GI_TYPELIB_PATH@ export LD_LIBRARY_PATH=@LD_LIBRARY_PATH@ +export GST_PLUGIN_PATH=@GST_PLUGIN_PATH@ export FAKE_GENICAM_PATH=@FAKE_GENICAM_PATH@ export ARV_PRODUCER_PATH=@ARV_PRODUCER_PATH@ diff --git a/tests/python/arv-gst-software-trigger.py b/tests/python/arv-gst-software-trigger.py new file mode 100755 index 000000000..8d6b5a604 --- /dev/null +++ b/tests/python/arv-gst-software-trigger.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +# If you have installed aravis in a non standard location, you may need +# to make GI_TYPELIB_PATH point to the correct location. For example: +# +# export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ +# +# You may also have to give the path to libaravis.so, using LD_PRELOAD or +# LD_LIBRARY_PATH. +# +# As this example alos uses the aravis gstreamer plugin, you may need to tell the path to the plugin using +# GST_PLUGIN_PATH. + +# Example of a gstreamer pipeline using a software trigger +# +# Author: WhaSukGO + +import gi +import threading +import time + +gi.require_version('Gst', '1.0') +from gi.repository import Gst, GLib + +# Initialize GStreamer +Gst.init(None) + +def main(): + + # Create GStreamer elements + aravissrc = Gst.ElementFactory.make("aravissrc", "source") + aravissrc.set_property("exposure", 1700) + aravissrc.set_property("do-timestamp", True) + aravissrc.set_property("trigger", "Software") + + bayer_caps = Gst.Caps.from_string("video/x-bayer,format=rggb,width=800,height=600") + bayer2rgb = Gst.ElementFactory.make("bayer2rgb", "bayer2rgb") + + videoconvert = Gst.ElementFactory.make("videoconvert", "videoconvert") + + videosink = Gst.ElementFactory.make("autovideosink", "videosink") + + # Create a new pipeline + pipeline = Gst.Pipeline.new("mypipeline") + + # Add elements to the pipeline + pipeline.add(aravissrc) + pipeline.add(bayer2rgb) + pipeline.add(videoconvert) + pipeline.add(videosink) + + # Link the elements with the capsfilter in between + aravissrc.link_filtered(bayer2rgb, bayer_caps) + bayer2rgb.link(videoconvert) + videoconvert.link(videosink) + + # Function to trigger the camera + def trigger_camera(): + while True: + time.sleep(0.5) # Sleep for 2 milliseconds (500fps) + aravissrc.emit("software-trigger") + + # Create and start the trigger thread + trigger_thread = threading.Thread(target=trigger_camera) + trigger_thread.daemon = True # Daemonize thread so it exits when the main program does + trigger_thread.start() + + # Start the pipeline + pipeline.set_state(Gst.State.PLAYING) + + # Run the pipeline + loop = GLib.MainLoop() + try: + loop.run() + except KeyboardInterrupt: + pass + + # Clean up + pipeline.set_state(Gst.State.NULL) + +if __name__ == "__main__": + main()