From 8880dbf3cf4d77d7077d396821675719dce142c7 Mon Sep 17 00:00:00 2001 From: yair Date: Sun, 16 Nov 2025 03:19:42 +0200 Subject: [PATCH] Add display option and device-id support to launch-ids.py - Added --display option for 1/4 sized preview window using autovideosink - Added --camera-id and --device-id options for camera selection - Added UDP control commands: SET_CAMERA_ID, GET_CAMERA_ID, SET_DEVICE_ID, GET_DEVICE_ID - Updated GStreamer idsueyesrc element to support device-id property - device-id uses IS_USE_DEVICE_ID flag for system enumeration - camera-id continues to use user-definable ID (0 for first found) - Updated STATUS command to include camera_id and device_id --- scripts/launch-ids.py | 101 +++++++++++++++++++++++++++++++++++- sys/idsueye/gstidsueyesrc.c | 20 ++++++- sys/idsueye/gstidsueyesrc.h | 1 + 3 files changed, 119 insertions(+), 3 deletions(-) diff --git a/scripts/launch-ids.py b/scripts/launch-ids.py index 80a0bb5..86e9b79 100644 --- a/scripts/launch-ids.py +++ b/scripts/launch-ids.py @@ -27,6 +27,8 @@ # - Optional control interface (default: UDP port 5001 on 0.0.0.0) # - Dynamic exposure control (1.0-1000.0 milliseconds, default: 10ms) # - Dynamic framerate control (1-20000 fps, default: 750fps) +# - Dynamic camera ID selection (0-254, default: 0 for first found) +# - Dynamic device ID selection (0-254, system enumeration ID) # - Configurable video cropping (default: crop 3 pixels from bottom) # - Verbose/quiet output modes # - Custom camera configuration files @@ -36,6 +38,10 @@ # GET_EXPOSURE - Get current exposure value # SET_FRAMERATE - Set framerate in Hz (e.g., 30) # GET_FRAMERATE - Get current framerate +# SET_CAMERA_ID - Set camera ID (0-254, 0 is first found) +# GET_CAMERA_ID - Get current camera ID +# SET_DEVICE_ID - Set device ID (0-254, system enumeration) +# GET_DEVICE_ID - Get current device ID # STATUS - Get pipeline status and current settings # # Example Control Usage: @@ -124,7 +130,8 @@ class ControlServer: return print(f"Control server listening on UDP port {self.port}") - print(" Commands: SET_EXPOSURE , GET_EXPOSURE, SET_FRAMERATE , GET_FRAMERATE, STATUS") + print(" Commands: SET_EXPOSURE , GET_EXPOSURE, SET_FRAMERATE , GET_FRAMERATE,") + print(" SET_CAMERA_ID , GET_CAMERA_ID, SET_DEVICE_ID , GET_DEVICE_ID, STATUS") while self.running: try: @@ -170,6 +177,14 @@ class ControlServer: return self.handle_set_framerate(parts) elif cmd == "GET_FRAMERATE": return self.handle_get_framerate() + elif cmd == "SET_CAMERA_ID": + return self.handle_set_camera_id(parts) + elif cmd == "GET_CAMERA_ID": + return self.handle_get_camera_id() + elif cmd == "SET_DEVICE_ID": + return self.handle_set_device_id(parts) + elif cmd == "GET_DEVICE_ID": + return self.handle_get_device_id() elif cmd == "STATUS": return self.handle_status() else: @@ -233,11 +248,67 @@ class ControlServer: except Exception as e: return f"ERROR: {str(e)}" + def handle_set_camera_id(self, parts): + """Handle SET_CAMERA_ID command""" + if len(parts) != 2: + return "ERROR INVALID_SYNTAX: Usage: SET_CAMERA_ID " + + try: + value = int(parts[1]) + if value < 0 or value > 254: + return "ERROR OUT_OF_RANGE: Camera ID must be 0-254" + + self.src.set_property("camera-id", value) + actual = self.src.get_property("camera-id") + return f"OK {actual}" + + except ValueError: + return "ERROR INVALID_SYNTAX: Camera ID must be an integer" + except Exception as e: + return f"ERROR: {str(e)}" + + def handle_get_camera_id(self): + """Handle GET_CAMERA_ID command""" + try: + value = self.src.get_property("camera-id") + return f"OK {value}" + except Exception as e: + return f"ERROR: {str(e)}" + + def handle_set_device_id(self, parts): + """Handle SET_DEVICE_ID command""" + if len(parts) != 2: + return "ERROR INVALID_SYNTAX: Usage: SET_DEVICE_ID " + + try: + value = int(parts[1]) + if value < 0 or value > 254: + return "ERROR OUT_OF_RANGE: Device ID must be 0-254" + + self.src.set_property("device-id", value) + actual = self.src.get_property("device-id") + return f"OK {actual}" + + except ValueError: + return "ERROR INVALID_SYNTAX: Device ID must be an integer" + except Exception as e: + return f"ERROR: {str(e)}" + + def handle_get_device_id(self): + """Handle GET_DEVICE_ID command""" + try: + value = self.src.get_property("device-id") + return f"OK {value}" + except Exception as e: + return f"ERROR: {str(e)}" + def handle_status(self): """Handle STATUS command""" try: exposure = self.src.get_property("exposure") framerate = self.src.get_property("framerate") + camera_id = self.src.get_property("camera-id") + device_id = self.src.get_property("device-id") # Get pipeline state state = "UNKNOWN" @@ -245,7 +316,7 @@ class ControlServer: _, current_state, _ = self.pipeline.get_state(0) state = current_state.value_nick.upper() - return f"OK exposure={exposure} framerate={framerate} state={state}" + return f"OK exposure={exposure} framerate={framerate} camera_id={camera_id} device_id={device_id} state={state}" except Exception as e: return f"ERROR: {str(e)}" @@ -297,6 +368,20 @@ Examples: metavar='HZ', help='Camera framerate in Hz (1.0-20200.0)' ) + camera_group.add_argument( + '--camera-id', + type=int, + default=0, + metavar='ID', + help='Camera ID (0 is first found, 0-254)' + ) + camera_group.add_argument( + '--device-id', + type=int, + default=0, + metavar='ID', + help='Device ID (system enumeration, 0 is first, 0-254)' + ) # Video processing video_group = parser.add_argument_group('Video Processing') @@ -379,6 +464,12 @@ Examples: if args.framerate < 1.0 or args.framerate > 20200.0: parser.error(f"Framerate must be between 1.0 and 20200.0 Hz, got {args.framerate}") + if args.camera_id < 0 or args.camera_id > 254: + parser.error(f"Camera ID must be between 0 and 254, got {args.camera_id}") + + if args.device_id < 0 or args.device_id > 254: + parser.error(f"Device ID must be between 0 and 254, got {args.device_id}") + if args.port < 1 or args.port > 65535: parser.error(f"UDP port must be between 1 and 65535, got {args.port}") @@ -433,6 +524,10 @@ src.set_property("exposure", args.exposure) # Frame rate src.set_property("framerate", args.framerate) +# Camera ID and Device ID +src.set_property("camera-id", args.camera_id) +src.set_property("device-id", args.device_id) + # Video crop to remove bottom pixels (if enabled) elements_to_link = [src] if args.crop_bottom > 0: @@ -572,6 +667,8 @@ if not args.quiet: print("IDS uEye Camera - Pipeline Configuration") print("=" * 60) print(f"Camera config: {args.config}") + print(f"Camera ID: {args.camera_id}") + print(f"Device ID: {args.device_id}") print(f"Exposure: {args.exposure} ms") print(f"Framerate: {args.framerate} Hz") if args.crop_bottom > 0: diff --git a/sys/idsueye/gstidsueyesrc.c b/sys/idsueye/gstidsueyesrc.c index eb41f92..ee9894f 100644 --- a/sys/idsueye/gstidsueyesrc.c +++ b/sys/idsueye/gstidsueyesrc.c @@ -68,6 +68,7 @@ enum { PROP_0, PROP_CAMERA_ID, + PROP_DEVICE_ID, PROP_CONFIG_FILE, PROP_NUM_CAPTURE_BUFFERS, PROP_TIMEOUT, @@ -76,6 +77,7 @@ enum }; #define DEFAULT_PROP_CAMERA_ID 0 +#define DEFAULT_PROP_DEVICE_ID 0 #define DEFAULT_PROP_CONFIG_FILE "" #define DEFAULT_PROP_NUM_CAPTURE_BUFFERS 3 #define DEFAULT_PROP_TIMEOUT 1000 @@ -131,6 +133,10 @@ gst_idsueyesrc_class_init (GstIdsueyeSrcClass * klass) g_param_spec_int ("camera-id", "Camera ID", "Camera ID (0 is first found)", 0, 254, DEFAULT_PROP_CAMERA_ID, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_DEVICE_ID, + g_param_spec_int ("device-id", "Device ID", + "Device ID (system enumeration, 0 is first)", 0, 254, DEFAULT_PROP_DEVICE_ID, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_CONFIG_FILE, g_param_spec_string ("config-file", "Config file", "Filepath of the uEye parameter file (*.ini)", @@ -184,6 +190,7 @@ gst_idsueyesrc_init (GstIdsueyeSrc * src) /* initialize member variables */ src->camera_id = DEFAULT_PROP_CAMERA_ID; + src->device_id = DEFAULT_PROP_DEVICE_ID; src->config_file = g_strdup (DEFAULT_PROP_CONFIG_FILE); src->num_capture_buffers = DEFAULT_PROP_NUM_CAPTURE_BUFFERS; src->timeout = DEFAULT_PROP_TIMEOUT; @@ -208,6 +215,9 @@ gst_idsueyesrc_set_property (GObject * object, guint property_id, case PROP_CAMERA_ID: src->camera_id = g_value_get_int (value); break; + case PROP_DEVICE_ID: + src->device_id = g_value_get_int (value); + break; case PROP_CONFIG_FILE: g_free (src->config_file); src->config_file = g_strdup (g_value_get_string (value)); @@ -249,6 +259,9 @@ gst_idsueyesrc_get_property (GObject * object, guint property_id, case PROP_CAMERA_ID: g_value_set_int (value, src->camera_id); break; + case PROP_DEVICE_ID: + g_value_set_int (value, src->device_id); + break; case PROP_CONFIG_FILE: g_value_set_string (value, src->config_file); break; @@ -482,7 +495,12 @@ gst_idsueyesrc_start (GstBaseSrc * bsrc) g_free (pucl); } - src->hCam = src->camera_id; + /* Use device-id if set (non-zero), otherwise use camera-id */ + if (src->device_id != 0) { + src->hCam = (src->device_id) | IS_USE_DEVICE_ID; + } else { + src->hCam = src->camera_id; + } ret = is_InitCamera (&src->hCam, NULL); if (ret != IS_SUCCESS) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, diff --git a/sys/idsueye/gstidsueyesrc.h b/sys/idsueye/gstidsueyesrc.h index 9c2a6b0..0870703 100644 --- a/sys/idsueye/gstidsueyesrc.h +++ b/sys/idsueye/gstidsueyesrc.h @@ -51,6 +51,7 @@ struct _GstIdsueyeSrc /* properties */ gint camera_id; + gint device_id; gchar *config_file; gint num_capture_buffers; gint timeout;