From 505f48f2373d86e67f243fa32a10296020c22532 Mon Sep 17 00:00:00 2001 From: Wojciech Kapsa Date: Mon, 26 Sep 2022 13:57:15 +0200 Subject: [PATCH] decklink: Add new persistent-id property and sort devices by persistent ID The order of the devices iterator from the SDK is undefined and can randomly change. Keep the device-number property for backwards compatibility and simplicity but prefer the persistent-id property and also use it for the device provider implementation. Part-of: --- .../docs/plugins/gst_plugins_cache.json | 56 ++++++++ .../sys/decklink/gstdecklink.cpp | 124 +++++++++++++++--- .../sys/decklink/gstdecklink.h | 12 +- .../sys/decklink/gstdecklinkaudiosink.cpp | 32 ++++- .../sys/decklink/gstdecklinkaudiosink.h | 2 + .../sys/decklink/gstdecklinkaudiosrc.cpp | 34 ++++- .../sys/decklink/gstdecklinkaudiosrc.h | 1 + .../decklink/gstdecklinkdeviceprovider.cpp | 4 +- .../sys/decklink/gstdecklinkvideosink.cpp | 30 ++++- .../sys/decklink/gstdecklinkvideosink.h | 1 + .../sys/decklink/gstdecklinkvideosrc.cpp | 30 ++++- .../sys/decklink/gstdecklinkvideosrc.h | 1 + 12 files changed, 296 insertions(+), 31 deletions(-) diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index 786addb02f..d95644b883 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -11460,6 +11460,20 @@ "readable": true, "type": "gchararray", "writable": false + }, + "persistent-id": { + "blurb": "Output device instance to use. Higher priority than \"device-number\".", + "conditionally-available": false, + "construct": true, + "construct-only": false, + "controllable": false, + "default": "18446744073709551615", + "max": "9223372036854775807", + "min": "-1", + "mutable": "null", + "readable": true, + "type": "gint64", + "writable": true } }, "rank": "none" @@ -11577,6 +11591,20 @@ "readable": true, "type": "gchararray", "writable": false + }, + "persistent-id": { + "blurb": "Output device instance to use. Higher priority than \"device-number\".", + "conditionally-available": false, + "construct": true, + "construct-only": false, + "controllable": false, + "default": "18446744073709551615", + "max": "9223372036854775807", + "min": "-1", + "mutable": "null", + "readable": true, + "type": "gint64", + "writable": true } }, "rank": "none" @@ -11706,6 +11734,20 @@ "type": "GstDecklinkModes", "writable": true }, + "persistent-id": { + "blurb": "Output device instance to use. Higher priority than \"device-number\".", + "conditionally-available": false, + "construct": true, + "construct-only": false, + "controllable": false, + "default": "18446744073709551615", + "max": "9223372036854775807", + "min": "-1", + "mutable": "null", + "readable": true, + "type": "gint64", + "writable": true + }, "profile": { "blurb": "Certain DeckLink devices such as the DeckLink 8K Pro, the DeckLink Quad 2 and the DeckLink Duo 2 support multiple profiles to configure the capture and playback behavior of its sub-devices.For the DeckLink Duo 2 and DeckLink Quad 2, a profile is shared between any 2 sub-devices that utilize the same connectors. For the DeckLink 8K Pro, a profile is shared between all 4 sub-devices. Any sub-devices that share a profile are considered to be part of the same profile group.DeckLink Duo 2 support configuration of the duplex mode of individual sub-devices.", "conditionally-available": false, @@ -11879,6 +11921,20 @@ "type": "gboolean", "writable": true }, + "persistent-id": { + "blurb": "Output device instance to use. Higher priority than \"device-number\".", + "conditionally-available": false, + "construct": true, + "construct-only": false, + "controllable": false, + "default": "18446744073709551615", + "max": "9223372036854775807", + "min": "-1", + "mutable": "null", + "readable": true, + "type": "gint64", + "writable": true + }, "profile": { "blurb": "Certain DeckLink devices such as the DeckLink 8K Pro, the DeckLink Quad 2 and the DeckLink Duo 2 support multiple profiles to configure the capture and playback behavior of its sub-devices.For the DeckLink Duo 2 and DeckLink Quad 2, a profile is shared between any 2 sub-devices that utilize the same connectors. For the DeckLink 8K Pro, a profile is shared between all 4 sub-devices. Any sub-devices that share a profile are considered to be part of the same profile group.DeckLink Duo 2 support configuration of the duplex mode of individual sub-devices.", "conditionally-available": false, diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp index 9c62239052..043805191e 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp @@ -34,6 +34,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug); #define GST_CAT_DEFAULT gst_decklink_debug +#define DEFAULT_PERSISTENT_ID (-1) GType gst_decklink_mode_get_type (void) @@ -1033,6 +1034,18 @@ static ProfileSetOperationResult gst_decklink_configure_profile (Device * static MappingFormatSetOperationResult gst_decklink_configure_mapping_format (Device * device, GstDecklinkMappingFormat mapping_format); +static gboolean +persistent_id_is_equal_input (const Device * a, const gint64 * b) +{ + return a->input.persistent_id == *b; +} + +static gboolean +persistent_id_is_equal_output (const Device * a, const gint64 * b) +{ + return a->output.persistent_id == *b; +} + class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback { private: @@ -1541,9 +1554,9 @@ static GPtrArray *devices; /* array of Device */ static GstDecklinkDevice * gst_decklink_device_new (const gchar * model_name, const gchar * display_name, - const gchar * serial_number, gboolean supports_format_detection, - GstCaps * video_caps, guint max_channels, gboolean video, gboolean capture, - guint device_number) + const gchar * serial_number, gint64 persistent_id, + gboolean supports_format_detection, GstCaps * video_caps, + guint max_channels, gboolean video, gboolean capture, guint device_number) { GstDevice *ret; gchar *name; @@ -1592,6 +1605,10 @@ gst_decklink_device_new (const gchar * model_name, const gchar * display_name, gst_structure_set (properties, "serial-number", G_TYPE_STRING, serial_number, NULL); + if (persistent_id) + gst_structure_set (properties, "persistent-id", G_TYPE_INT64, + persistent_id, NULL); + ret = GST_DEVICE (g_object_new (GST_TYPE_DECKLINK_DEVICE, "display-name", name, "device-class", device_class, "caps", caps, "properties", properties, @@ -1603,11 +1620,19 @@ gst_decklink_device_new (const gchar * model_name, const gchar * display_name, GST_DECKLINK_DEVICE (ret)->video = video; GST_DECKLINK_DEVICE (ret)->capture = capture; - GST_DECKLINK_DEVICE (ret)->device_number = device_number; + GST_DECKLINK_DEVICE (ret)->persistent_id = persistent_id; return GST_DECKLINK_DEVICE (ret); } +static gint +compare_persistent_id (gconstpointer a, gconstpointer b) +{ + const Device *const dev1 = *(Device **) a; + const Device *const dev2 = *(Device **) b; + return dev1->input.persistent_id - dev2->input.persistent_id; +} + static gpointer init_devices (gpointer data) { @@ -1646,6 +1671,7 @@ init_devices (gpointer data) gchar *model_name = NULL; gchar *display_name = NULL; gchar *serial_number = NULL; + gint64 persistent_id = 0; gboolean supports_format_detection = 0; gint64 max_channels = 2; GstCaps *video_input_caps = gst_caps_new_empty (); @@ -1776,12 +1802,28 @@ init_devices (gpointer data) } else { bool tmp_bool = false; int64_t tmp_int = 2; + int64_t tmp_int_persistent_id = 0; dev->input.attributes->GetInt (BMDDeckLinkMaximumAudioChannels, &tmp_int); dev->input.attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection, &tmp_bool); supports_format_detection = tmp_bool; max_channels = tmp_int; + + ret = + dev->input.attributes->GetInt (BMDDeckLinkPersistentID, + &tmp_int_persistent_id); + if (ret == S_OK) { + persistent_id = tmp_int_persistent_id; + dev->output.persistent_id = persistent_id; + dev->input.persistent_id = persistent_id; + GST_DEBUG ("device %d has persistent id %" G_GINT64_FORMAT, i, persistent_id); + } else { + persistent_id = i; + dev->output.persistent_id = i; + dev->input.persistent_id = i; + GST_DEBUG ("device %d does not have persistent id. Value set to %d", i, i); + } } decklink->GetModelName ((COMSTR_T *) & model_name); @@ -1794,22 +1836,22 @@ init_devices (gpointer data) if (capture) { dev->devices[0] = gst_decklink_device_new (model_name, display_name, serial_number, - supports_format_detection, video_input_caps, max_channels, TRUE, TRUE, - i); + persistent_id, supports_format_detection, video_input_caps, + max_channels, TRUE, TRUE, i); dev->devices[1] = gst_decklink_device_new (model_name, display_name, serial_number, - supports_format_detection, video_input_caps, max_channels, FALSE, - TRUE, i); + persistent_id, supports_format_detection, video_input_caps, + max_channels, FALSE, TRUE, i); } if (output) { dev->devices[2] = gst_decklink_device_new (model_name, display_name, serial_number, - supports_format_detection, video_output_caps, max_channels, TRUE, - FALSE, i); + persistent_id, supports_format_detection, video_output_caps, + max_channels, TRUE, FALSE, i); dev->devices[3] = gst_decklink_device_new (model_name, display_name, serial_number, - supports_format_detection, video_output_caps, max_channels, FALSE, - FALSE, i); + persistent_id, supports_format_detection, video_output_caps, + max_channels, FALSE, FALSE, i); } if (model_name) @@ -1838,6 +1880,8 @@ init_devices (gpointer data) iterator->Release (); + g_ptr_array_sort (devices, compare_persistent_id); + return NULL; } @@ -1875,16 +1919,28 @@ gst_decklink_get_devices (void) } GstDecklinkOutput * -gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio) +gst_decklink_acquire_nth_output (gint n, gint64 persistent_id, + GstElement * sink, gboolean is_audio) { GstDecklinkOutput *output; Device *device; + guint found_index; g_once (&devices_once, init_devices, NULL); if (devices == NULL) return NULL; + if (persistent_id != DEFAULT_PERSISTENT_ID) { + if (g_ptr_array_find_with_equal_func (devices, &persistent_id, + (GEqualFunc) persistent_id_is_equal_output, &found_index)) { + n = found_index; + GST_DEBUG ("Persistent ID: %" G_GINT64_FORMAT ", used", persistent_id); + } else { + return NULL; + } + } + if (n < 0 || (guint) n >= devices->len) return NULL; @@ -1924,14 +1980,26 @@ gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio) } void -gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio) +gst_decklink_release_nth_output (gint n, gint64 persistent_id, + GstElement * sink, gboolean is_audio) { GstDecklinkOutput *output; Device *device; + guint found_index; if (devices == NULL) return; + if (persistent_id != DEFAULT_PERSISTENT_ID) { + if (g_ptr_array_find_with_equal_func (devices, &persistent_id, + (GEqualFunc) persistent_id_is_equal_output, &found_index)) { + n = found_index; + GST_DEBUG ("Persistent ID: %" G_GINT64_FORMAT ", used", persistent_id); + } else { + return; + } + } + if (n < 0 || (guint) n >= devices->len) return; @@ -1953,16 +2021,28 @@ gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio) } GstDecklinkInput * -gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio) +gst_decklink_acquire_nth_input (gint n, gint64 persistent_id, GstElement * src, + gboolean is_audio) { GstDecklinkInput *input; Device *device; + guint found_index; g_once (&devices_once, init_devices, NULL); if (devices == NULL) return NULL; + if (persistent_id != DEFAULT_PERSISTENT_ID) { + if (g_ptr_array_find_with_equal_func (devices, &persistent_id, + (GEqualFunc) persistent_id_is_equal_input, &found_index)) { + n = found_index; + GST_DEBUG ("Persistent ID: %" G_GINT64_FORMAT ", used", persistent_id); + } else { + return NULL; + } + } + if (n < 0 || (guint) n >= devices->len) return NULL; @@ -2001,14 +2081,26 @@ gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio) } void -gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio) +gst_decklink_release_nth_input (gint n, gint64 persistent_id, GstElement * src, + gboolean is_audio) { GstDecklinkInput *input; Device *device; + guint found_index; if (devices == NULL) return; + if (persistent_id != DEFAULT_PERSISTENT_ID) { + if (g_ptr_array_find_with_equal_func (devices, &persistent_id, + (GEqualFunc) persistent_id_is_equal_input, &found_index)) { + n = found_index; + GST_DEBUG ("Persistent ID: %" G_GINT64_FORMAT ", used", persistent_id); + } else { + return; + } + } + if (n < 0 || (guint) n >= devices->len) return; diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h index 5b282728b0..2d1d0a649d 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h @@ -401,6 +401,7 @@ struct _GstDecklinkOutput { IDeckLinkKeyer *keyer; gchar *hw_serial_number; + gint64 persistent_id; GstClock *clock; GstClockTime clock_start_time, clock_last_time, clock_epoch; @@ -431,6 +432,7 @@ struct _GstDecklinkInput { IDeckLinkProfileAttributes *attributes; gchar *hw_serial_number; + gint64 persistent_id; /* Everything below protected by mutex */ GMutex lock; @@ -452,11 +454,11 @@ struct _GstDecklinkInput { void (*start_streams) (GstElement *videosrc); }; -GstDecklinkOutput * gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio); -void gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio); +GstDecklinkOutput * gst_decklink_acquire_nth_output (gint n, gint64 persistent_id, GstElement * sink, gboolean is_audio); +void gst_decklink_release_nth_output (gint n, gint64 persistent_id, GstElement * sink, gboolean is_audio); -GstDecklinkInput * gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio); -void gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio); +GstDecklinkInput * gst_decklink_acquire_nth_input (gint n, gint64 persistent_id, GstElement * src, gboolean is_audio); +void gst_decklink_release_nth_input (gint n, gint64 persistent_id, GstElement * src, gboolean is_audio); const GstDecklinkMode * gst_decklink_find_mode_for_caps (GstCaps * caps); const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format); @@ -479,7 +481,7 @@ struct _GstDecklinkDevice GstDevice parent; gboolean video; gboolean capture; - guint device_number; + gint64 persistent_id; }; GType gst_decklink_device_get_type (void); diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.cpp index 65991ba3a8..b82f63d09a 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.cpp @@ -52,6 +52,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_audio_sink_debug); // Microseconds for audiobasesink compatibility... #define DEFAULT_BUFFER_TIME (50 * GST_MSECOND / 1000) +#define DEFAULT_PERSISTENT_ID (-1) + enum { PROP_0, @@ -60,6 +62,7 @@ enum PROP_ALIGNMENT_THRESHOLD, PROP_DISCONT_WAIT, PROP_BUFFER_TIME, + PROP_PERSISTENT_ID }; static void gst_decklink_audio_sink_set_property (GObject * object, @@ -141,6 +144,23 @@ gst_decklink_audio_sink_class_init (GstDecklinkAudioSinkClass * klass) (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + /** + * GstDecklinkAudioSink:persistent-id + * + * Decklink device to use. Higher priority than "device-number". + * BMDDeckLinkPersistentID is a device specific, 32-bit unique identifier. + * It is stable even when the device is plugged in a different connector, + * across reboots, and when plugged into different computers. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_PERSISTENT_ID, + g_param_spec_int64 ("persistent-id", "Persistent id", + "Output device instance to use. Higher priority than \"device-number\".", + DEFAULT_PERSISTENT_ID, G_MAXINT64, DEFAULT_PERSISTENT_ID, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); + g_object_class_install_property (gobject_class, PROP_HW_SERIAL_NUMBER, g_param_spec_string ("hw-serial-number", "Hardware serial number", "The serial number (hardware ID) of the Decklink card", @@ -188,6 +208,8 @@ gst_decklink_audio_sink_init (GstDecklinkAudioSink * self) DEFAULT_DISCONT_WAIT); self->buffer_time = DEFAULT_BUFFER_TIME * 1000; + self->persistent_id = DEFAULT_PERSISTENT_ID; + gst_base_sink_set_max_lateness (GST_BASE_SINK_CAST (self), 20 * GST_MSECOND); } @@ -218,6 +240,9 @@ gst_decklink_audio_sink_set_property (GObject * object, guint property_id, self->buffer_time = g_value_get_uint64 (value) * 1000; GST_OBJECT_UNLOCK (self); break; + case PROP_PERSISTENT_ID: + self->persistent_id = g_value_get_int64 (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -257,6 +282,9 @@ gst_decklink_audio_sink_get_property (GObject * object, guint property_id, g_value_set_uint64 (value, self->buffer_time / 1000); GST_OBJECT_UNLOCK (self); break; + case PROP_PERSISTENT_ID: + g_value_set_int64 (value, self->persistent_id); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -799,7 +827,7 @@ gst_decklink_audio_sink_open (GstBaseSink * bsink) GST_DEBUG_OBJECT (self, "Starting"); self->output = - gst_decklink_acquire_nth_output (self->device_number, + gst_decklink_acquire_nth_output (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), TRUE); if (!self->output) { GST_ERROR_OBJECT (self, "Failed to acquire output"); @@ -827,7 +855,7 @@ gst_decklink_audio_sink_close (GstBaseSink * bsink) g_mutex_unlock (&self->output->lock); self->output->output->DisableAudioOutput (); - gst_decklink_release_nth_output (self->device_number, + gst_decklink_release_nth_output (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), TRUE); self->output = NULL; } diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.h index a9a94e7757..f117270d21 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.h @@ -58,6 +58,8 @@ struct _GstDecklinkAudioSink GstAudioStreamAlign *stream_align; GstAudioResampler *resampler; guint resampler_in_rate, resampler_out_rate; + + gint64 persistent_id; }; struct _GstDecklinkAudioSinkClass diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.cpp index 50ad5cc138..95de53a2b1 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.cpp @@ -57,6 +57,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_audio_src_debug); #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) ) #endif +#define DEFAULT_PERSISTENT_ID (-1) + enum { PROP_0, @@ -66,7 +68,8 @@ enum PROP_DISCONT_WAIT, PROP_BUFFER_SIZE, PROP_CHANNELS, - PROP_HW_SERIAL_NUMBER + PROP_HW_SERIAL_NUMBER, + PROP_PERSISTENT_ID }; static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("src", @@ -180,6 +183,23 @@ gst_decklink_audio_src_class_init (GstDecklinkAudioSrcClass * klass) (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + /** + * GstDecklinkAudioSrc:persistent-id + * + * Decklink device to use. Higher priority than "device-number". + * BMDDeckLinkPersistentID is a device specific, 32-bit unique identifier. + * It is stable even when the device is plugged in a different connector, + * across reboots, and when plugged into different computers. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_PERSISTENT_ID, + g_param_spec_int64 ("persistent-id", "Persistent id", + "Output device instance to use. Higher priority than \"device-number\".", + DEFAULT_PERSISTENT_ID, G_MAXINT64, DEFAULT_PERSISTENT_ID, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); + g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", "Timestamp alignment threshold in nanoseconds", 0, @@ -246,6 +266,8 @@ gst_decklink_audio_src_init (GstDecklinkAudioSrc * self) self->skipped_last = 0; self->skip_from_timestamp = GST_CLOCK_TIME_NONE; self->skip_to_timestamp = GST_CLOCK_TIME_NONE; + + self->persistent_id = DEFAULT_PERSISTENT_ID; } void @@ -274,6 +296,9 @@ gst_decklink_audio_src_set_property (GObject * object, guint property_id, case PROP_CHANNELS: self->channels = (GstDecklinkAudioChannelsEnum) g_value_get_enum (value); break; + case PROP_PERSISTENT_ID: + self->persistent_id = g_value_get_int64 (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -311,6 +336,9 @@ gst_decklink_audio_src_get_property (GObject * object, guint property_id, else g_value_set_string (value, NULL); break; + case PROP_PERSISTENT_ID: + g_value_set_int64 (value, self->persistent_id); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -939,7 +967,7 @@ gst_decklink_audio_src_open (GstDecklinkAudioSrc * self) GST_DEBUG_OBJECT (self, "Opening"); self->input = - gst_decklink_acquire_nth_input (self->device_number, + gst_decklink_acquire_nth_input (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), TRUE); if (!self->input) { GST_ERROR_OBJECT (self, "Failed to acquire input"); @@ -986,7 +1014,7 @@ gst_decklink_audio_src_close (GstDecklinkAudioSrc * self) self->input->got_audio_packet = NULL; g_mutex_unlock (&self->input->lock); - gst_decklink_release_nth_input (self->device_number, + gst_decklink_release_nth_input (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), TRUE); self->input = NULL; } diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.h index bda465506b..9b1e69c12a 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.h @@ -52,6 +52,7 @@ struct _GstDecklinkAudioSrc GstDecklinkModeEnum mode; GstDecklinkAudioConnectionEnum connection; gint device_number; + gint64 persistent_id; GstDecklinkAudioChannelsEnum channels; gint64 channels_found; diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkdeviceprovider.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkdeviceprovider.cpp index 3a98705932..7c20daae9c 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkdeviceprovider.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkdeviceprovider.cpp @@ -24,6 +24,8 @@ #include "gstdecklinkdeviceprovider.h" #include "gstdecklink.h" +#define DEFAULT_PERSISTENT_ID (-1) + G_DEFINE_TYPE (GstDecklinkDeviceProvider, gst_decklink_device_provider, GST_TYPE_DEVICE_PROVIDER); GST_DEVICE_PROVIDER_REGISTER_DEFINE (decklinkdeviceprovider, "decklinkdeviceprovider", @@ -77,7 +79,7 @@ gst_decklink_device_create_element (GstDevice * device, const gchar * name) } if (ret) { - g_object_set (ret, "device-number", self->device_number, NULL); + g_object_set (ret, "persistent-id", self->persistent_id, NULL); } return ret; diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp index bfbeafd31b..8c1ca9fd7f 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp @@ -140,6 +140,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_sink_debug); #define GST_CAT_DEFAULT gst_decklink_video_sink_debug +#define DEFAULT_PERSISTENT_ID (-1) + class GStreamerVideoOutputCallback:public IDeckLinkVideoOutputCallback { public: @@ -521,6 +523,7 @@ enum PROP_CC_LINE, PROP_AFD_BAR_LINE, PROP_MAPPING_FORMAT, + PROP_PERSISTENT_ID }; static void gst_decklink_video_sink_set_property (GObject * object, @@ -612,6 +615,22 @@ gst_decklink_video_sink_class_init (GstDecklinkVideoSinkClass * klass) "Output device instance to use", 0, G_MAXINT, 0, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + /** + * GstDecklinkVideoSink:persistent-id + * + * Decklink device to use. Higher priority than "device-number". + * BMDDeckLinkPersistentID is a device specific, 32-bit unique identifier. + * It is stable even when the device is plugged in a different connector, + * across reboots, and when plugged into different computers. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_PERSISTENT_ID, + g_param_spec_int64 ("persistent-id", "Persistent id", + "Output device instance to use. Higher priority than \"device-number\".", + DEFAULT_PERSISTENT_ID, G_MAXINT64, DEFAULT_PERSISTENT_ID, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); g_object_class_install_property (gobject_class, PROP_VIDEO_FORMAT, g_param_spec_enum ("video-format", "Video format", @@ -723,6 +742,7 @@ gst_decklink_video_sink_init (GstDecklinkVideoSink * self) { self->mode = GST_DECKLINK_MODE_NTSC; self->device_number = 0; + self->persistent_id = DEFAULT_PERSISTENT_ID; self->video_format = GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV; self->profile_id = GST_DECKLINK_PROFILE_ID_DEFAULT; /* VITC is legacy, we should expect RP188 in modern use cases */ @@ -789,6 +809,9 @@ gst_decklink_video_sink_set_property (GObject * object, guint property_id, self->mapping_format = (GstDecklinkMappingFormat) g_value_get_enum (value); break; + case PROP_PERSISTENT_ID: + self->persistent_id = g_value_get_int64 (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -840,6 +863,9 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id, case PROP_MAPPING_FORMAT: g_value_set_enum (value, self->mapping_format); break; + case PROP_PERSISTENT_ID: + g_value_set_int64 (value, self->persistent_id); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -1673,7 +1699,7 @@ gst_decklink_video_sink_open (GstBaseSink * bsink) GST_DEBUG_OBJECT (self, "Starting"); self->output = - gst_decklink_acquire_nth_output (self->device_number, + gst_decklink_acquire_nth_output (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), FALSE); if (!self->output) { GST_ERROR_OBJECT (self, "Failed to acquire output"); @@ -1718,7 +1744,7 @@ gst_decklink_video_sink_close (GstBaseSink * bsink) g_mutex_unlock (&self->output->lock); self->output->output->DisableVideoOutput (); - gst_decklink_release_nth_output (self->device_number, + gst_decklink_release_nth_output (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), FALSE); self->output = NULL; } diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h index 4255a8a42e..a2fde1bcb4 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h @@ -51,6 +51,7 @@ struct _GstDecklinkVideoSink GstDecklinkModeEnum mode; gint device_number; + gint64 persistent_id; GstDecklinkVideoFormat video_format; GstDecklinkProfileId profile_id; BMDTimecodeFormat timecode_format; diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.cpp b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.cpp index 1bdff4e9fe..e3df97c4ad 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.cpp +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.cpp @@ -149,6 +149,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug); #define DEFAULT_DROP_NO_SIGNAL_FRAMES (FALSE) #define DEFAULT_OUTPUT_CC (FALSE) #define DEFAULT_OUTPUT_AFD_BAR (FALSE) +#define DEFAULT_PERSISTENT_ID (-1) #ifndef ABSDIFF #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) ) @@ -169,6 +170,7 @@ enum PROP_DROP_NO_SIGNAL_FRAMES, PROP_SIGNAL, PROP_HW_SERIAL_NUMBER, + PROP_PERSISTENT_ID, PROP_OUTPUT_CC, PROP_OUTPUT_AFD_BAR, }; @@ -290,6 +292,23 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass) (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + /** + * GstDecklinkVideoSrc:persistent-id + * + * Decklink device to use. Higher priority than "device-number". + * BMDDeckLinkPersistentID is a device specific, 32-bit unique identifier. + * It is stable even when the device is plugged in a different connector, + * across reboots, and when plugged into different computers. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_PERSISTENT_ID, + g_param_spec_int64 ("persistent-id", "Persistent id", + "Output device instance to use. Higher priority than \"device-number\".", + DEFAULT_PERSISTENT_ID, G_MAXINT64, DEFAULT_PERSISTENT_ID, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); + g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE, g_param_spec_uint ("buffer-size", "Buffer Size", "Size of internal buffer in number of video frames", 1, @@ -396,6 +415,7 @@ gst_decklink_video_src_init (GstDecklinkVideoSrc * self) self->caps_format = bmdFormat8BitYUV; self->connection = DEFAULT_CONNECTION; self->device_number = 0; + self->persistent_id = DEFAULT_PERSISTENT_ID; self->buffer_size = DEFAULT_BUFFER_SIZE; self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO; self->profile_id = GST_DECKLINK_PROFILE_ID_DEFAULT; @@ -490,6 +510,9 @@ gst_decklink_video_src_set_property (GObject * object, guint property_id, case PROP_DROP_NO_SIGNAL_FRAMES: self->drop_no_signal_frames = g_value_get_boolean (value); break; + case PROP_PERSISTENT_ID: + self->persistent_id = g_value_get_int64 (value); + break; case PROP_OUTPUT_CC: self->output_cc = g_value_get_boolean (value); break; @@ -540,6 +563,9 @@ gst_decklink_video_src_get_property (GObject * object, guint property_id, case PROP_DROP_NO_SIGNAL_FRAMES: g_value_set_boolean (value, self->drop_no_signal_frames); break; + case PROP_PERSISTENT_ID: + g_value_set_int64 (value, self->persistent_id); + break; case PROP_SIGNAL: g_value_set_boolean (value, self->signal_state == SIGNAL_STATE_AVAILABLE); break; @@ -1523,7 +1549,7 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self) GST_DEBUG_OBJECT (self, "Opening"); self->input = - gst_decklink_acquire_nth_input (self->device_number, + gst_decklink_acquire_nth_input (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), FALSE); if (!self->input) { GST_ERROR_OBJECT (self, "Failed to acquire input"); @@ -1559,7 +1585,7 @@ gst_decklink_video_src_close (GstDecklinkVideoSrc * self) self->input->start_streams = NULL; g_mutex_unlock (&self->input->lock); - gst_decklink_release_nth_input (self->device_number, + gst_decklink_release_nth_input (self->device_number, self->persistent_id, GST_ELEMENT_CAST (self), FALSE); self->input = NULL; } diff --git a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.h b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.h index 5e9876687c..03c455a3ff 100644 --- a/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.h +++ b/subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.h @@ -62,6 +62,7 @@ struct _GstDecklinkVideoSrc BMDPixelFormat caps_format; GstDecklinkConnectionEnum connection; gint device_number; + gint64 persistent_id; gboolean output_stream_time; GstClockTime skip_first_time; gboolean drop_no_signal_frames;