decklink: Add new persistent-id property and sort devices by persistent ID
authorWojciech Kapsa <wojciech.kapsa@medvc.eu>
Mon, 26 Sep 2022 11:57:15 +0000 (13:57 +0200)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Thu, 6 Oct 2022 15:50:11 +0000 (15:50 +0000)
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3078>

12 files changed:
subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json
subprojects/gst-plugins-bad/sys/decklink/gstdecklink.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklink.h
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosink.h
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkaudiosrc.h
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkdeviceprovider.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosink.h
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.cpp
subprojects/gst-plugins-bad/sys/decklink/gstdecklinkvideosrc.h

index 786addb..d95644b 100644 (file)
                         "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"
                         "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"
                         "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,
                         "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,
index 9c62239..0438051 100644 (file)
@@ -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;
 
index 5b28272..2d1d0a6 100644 (file)
@@ -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);
index 65991ba..b82f63d 100644 (file)
@@ -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;
   }
index a9a94e7..f117270 100644 (file)
@@ -58,6 +58,8 @@ struct _GstDecklinkAudioSink
   GstAudioStreamAlign *stream_align;
   GstAudioResampler *resampler;
   guint resampler_in_rate, resampler_out_rate;
+
+  gint64 persistent_id;
 };
 
 struct _GstDecklinkAudioSinkClass
index 50ad5cc..95de53a 100644 (file)
@@ -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;
   }
index bda4655..9b1e69c 100644 (file)
@@ -52,6 +52,7 @@ struct _GstDecklinkAudioSrc
   GstDecklinkModeEnum mode;
   GstDecklinkAudioConnectionEnum connection;
   gint device_number;
+  gint64 persistent_id;
   GstDecklinkAudioChannelsEnum channels;
   gint64 channels_found;
 
index 3a98705..7c20daa 100644 (file)
@@ -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;
index bfbeafd..8c1ca9f 100644 (file)
 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;
   }
index 4255a8a..a2fde1b 100644 (file)
@@ -51,6 +51,7 @@ struct _GstDecklinkVideoSink
 
   GstDecklinkModeEnum mode;
   gint device_number;
+  gint64 persistent_id;
   GstDecklinkVideoFormat video_format;
   GstDecklinkProfileId profile_id;
   BMDTimecodeFormat timecode_format;
index 1bdff4e..e3df97c 100644 (file)
@@ -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;
   }
index 5e98766..03c455a 100644 (file)
@@ -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;