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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3078>
This commit is contained in:
Wojciech Kapsa 2022-09-26 13:57:15 +02:00 committed by GStreamer Marge Bot
parent 67caa45a4c
commit 505f48f237
12 changed files with 296 additions and 31 deletions

View file

@ -11460,6 +11460,20 @@
"readable": true, "readable": true,
"type": "gchararray", "type": "gchararray",
"writable": false "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" "rank": "none"
@ -11577,6 +11591,20 @@
"readable": true, "readable": true,
"type": "gchararray", "type": "gchararray",
"writable": false "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" "rank": "none"
@ -11706,6 +11734,20 @@
"type": "GstDecklinkModes", "type": "GstDecklinkModes",
"writable": true "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": { "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.", "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, "conditionally-available": false,
@ -11879,6 +11921,20 @@
"type": "gboolean", "type": "gboolean",
"writable": true "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": { "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.", "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, "conditionally-available": false,

View file

@ -34,6 +34,7 @@
GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug); GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug);
#define GST_CAT_DEFAULT gst_decklink_debug #define GST_CAT_DEFAULT gst_decklink_debug
#define DEFAULT_PERSISTENT_ID (-1)
GType GType
gst_decklink_mode_get_type (void) 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 * static MappingFormatSetOperationResult gst_decklink_configure_mapping_format (Device *
device, GstDecklinkMappingFormat mapping_format); 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 class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback
{ {
private: private:
@ -1541,9 +1554,9 @@ static GPtrArray *devices; /* array of Device */
static GstDecklinkDevice * static GstDecklinkDevice *
gst_decklink_device_new (const gchar * model_name, const gchar * display_name, gst_decklink_device_new (const gchar * model_name, const gchar * display_name,
const gchar * serial_number, gboolean supports_format_detection, const gchar * serial_number, gint64 persistent_id,
GstCaps * video_caps, guint max_channels, gboolean video, gboolean capture, gboolean supports_format_detection, GstCaps * video_caps,
guint device_number) guint max_channels, gboolean video, gboolean capture, guint device_number)
{ {
GstDevice *ret; GstDevice *ret;
gchar *name; 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, gst_structure_set (properties, "serial-number", G_TYPE_STRING,
serial_number, NULL); 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, ret = GST_DEVICE (g_object_new (GST_TYPE_DECKLINK_DEVICE,
"display-name", name, "display-name", name,
"device-class", device_class, "caps", caps, "properties", properties, "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)->video = video;
GST_DECKLINK_DEVICE (ret)->capture = capture; 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); 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 static gpointer
init_devices (gpointer data) init_devices (gpointer data)
{ {
@ -1646,6 +1671,7 @@ init_devices (gpointer data)
gchar *model_name = NULL; gchar *model_name = NULL;
gchar *display_name = NULL; gchar *display_name = NULL;
gchar *serial_number = NULL; gchar *serial_number = NULL;
gint64 persistent_id = 0;
gboolean supports_format_detection = 0; gboolean supports_format_detection = 0;
gint64 max_channels = 2; gint64 max_channels = 2;
GstCaps *video_input_caps = gst_caps_new_empty (); GstCaps *video_input_caps = gst_caps_new_empty ();
@ -1776,12 +1802,28 @@ init_devices (gpointer data)
} else { } else {
bool tmp_bool = false; bool tmp_bool = false;
int64_t tmp_int = 2; int64_t tmp_int = 2;
int64_t tmp_int_persistent_id = 0;
dev->input.attributes->GetInt (BMDDeckLinkMaximumAudioChannels, &tmp_int); dev->input.attributes->GetInt (BMDDeckLinkMaximumAudioChannels, &tmp_int);
dev->input.attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection, dev->input.attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection,
&tmp_bool); &tmp_bool);
supports_format_detection = tmp_bool; supports_format_detection = tmp_bool;
max_channels = tmp_int; 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); decklink->GetModelName ((COMSTR_T *) & model_name);
@ -1794,22 +1836,22 @@ init_devices (gpointer data)
if (capture) { if (capture) {
dev->devices[0] = dev->devices[0] =
gst_decklink_device_new (model_name, display_name, serial_number, gst_decklink_device_new (model_name, display_name, serial_number,
supports_format_detection, video_input_caps, max_channels, TRUE, TRUE, persistent_id, supports_format_detection, video_input_caps,
i); max_channels, TRUE, TRUE, i);
dev->devices[1] = dev->devices[1] =
gst_decklink_device_new (model_name, display_name, serial_number, gst_decklink_device_new (model_name, display_name, serial_number,
supports_format_detection, video_input_caps, max_channels, FALSE, persistent_id, supports_format_detection, video_input_caps,
TRUE, i); max_channels, FALSE, TRUE, i);
} }
if (output) { if (output) {
dev->devices[2] = dev->devices[2] =
gst_decklink_device_new (model_name, display_name, serial_number, gst_decklink_device_new (model_name, display_name, serial_number,
supports_format_detection, video_output_caps, max_channels, TRUE, persistent_id, supports_format_detection, video_output_caps,
FALSE, i); max_channels, TRUE, FALSE, i);
dev->devices[3] = dev->devices[3] =
gst_decklink_device_new (model_name, display_name, serial_number, gst_decklink_device_new (model_name, display_name, serial_number,
supports_format_detection, video_output_caps, max_channels, FALSE, persistent_id, supports_format_detection, video_output_caps,
FALSE, i); max_channels, FALSE, FALSE, i);
} }
if (model_name) if (model_name)
@ -1838,6 +1880,8 @@ init_devices (gpointer data)
iterator->Release (); iterator->Release ();
g_ptr_array_sort (devices, compare_persistent_id);
return NULL; return NULL;
} }
@ -1875,16 +1919,28 @@ gst_decklink_get_devices (void)
} }
GstDecklinkOutput * 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; GstDecklinkOutput *output;
Device *device; Device *device;
guint found_index;
g_once (&devices_once, init_devices, NULL); g_once (&devices_once, init_devices, NULL);
if (devices == NULL) if (devices == NULL)
return 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) if (n < 0 || (guint) n >= devices->len)
return NULL; return NULL;
@ -1924,14 +1980,26 @@ gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio)
} }
void 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; GstDecklinkOutput *output;
Device *device; Device *device;
guint found_index;
if (devices == NULL) if (devices == NULL)
return; 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) if (n < 0 || (guint) n >= devices->len)
return; return;
@ -1953,16 +2021,28 @@ gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio)
} }
GstDecklinkInput * 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; GstDecklinkInput *input;
Device *device; Device *device;
guint found_index;
g_once (&devices_once, init_devices, NULL); g_once (&devices_once, init_devices, NULL);
if (devices == NULL) if (devices == NULL)
return 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) if (n < 0 || (guint) n >= devices->len)
return NULL; return NULL;
@ -2001,14 +2081,26 @@ gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio)
} }
void 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; GstDecklinkInput *input;
Device *device; Device *device;
guint found_index;
if (devices == NULL) if (devices == NULL)
return; 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) if (n < 0 || (guint) n >= devices->len)
return; return;

View file

@ -401,6 +401,7 @@ struct _GstDecklinkOutput {
IDeckLinkKeyer *keyer; IDeckLinkKeyer *keyer;
gchar *hw_serial_number; gchar *hw_serial_number;
gint64 persistent_id;
GstClock *clock; GstClock *clock;
GstClockTime clock_start_time, clock_last_time, clock_epoch; GstClockTime clock_start_time, clock_last_time, clock_epoch;
@ -431,6 +432,7 @@ struct _GstDecklinkInput {
IDeckLinkProfileAttributes *attributes; IDeckLinkProfileAttributes *attributes;
gchar *hw_serial_number; gchar *hw_serial_number;
gint64 persistent_id;
/* Everything below protected by mutex */ /* Everything below protected by mutex */
GMutex lock; GMutex lock;
@ -452,11 +454,11 @@ struct _GstDecklinkInput {
void (*start_streams) (GstElement *videosrc); void (*start_streams) (GstElement *videosrc);
}; };
GstDecklinkOutput * gst_decklink_acquire_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, 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); GstDecklinkInput * gst_decklink_acquire_nth_input (gint n, gint64 persistent_id, GstElement * src, gboolean is_audio);
void gst_decklink_release_nth_input (gint n, 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_for_caps (GstCaps * caps);
const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format); const GstDecklinkMode * gst_decklink_find_mode_and_format_for_caps (GstCaps * caps, BMDPixelFormat * format);
@ -479,7 +481,7 @@ struct _GstDecklinkDevice
GstDevice parent; GstDevice parent;
gboolean video; gboolean video;
gboolean capture; gboolean capture;
guint device_number; gint64 persistent_id;
}; };
GType gst_decklink_device_get_type (void); GType gst_decklink_device_get_type (void);

View file

@ -52,6 +52,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_audio_sink_debug);
// Microseconds for audiobasesink compatibility... // Microseconds for audiobasesink compatibility...
#define DEFAULT_BUFFER_TIME (50 * GST_MSECOND / 1000) #define DEFAULT_BUFFER_TIME (50 * GST_MSECOND / 1000)
#define DEFAULT_PERSISTENT_ID (-1)
enum enum
{ {
PROP_0, PROP_0,
@ -60,6 +62,7 @@ enum
PROP_ALIGNMENT_THRESHOLD, PROP_ALIGNMENT_THRESHOLD,
PROP_DISCONT_WAIT, PROP_DISCONT_WAIT,
PROP_BUFFER_TIME, PROP_BUFFER_TIME,
PROP_PERSISTENT_ID
}; };
static void gst_decklink_audio_sink_set_property (GObject * object, 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 | (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT))); 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_object_class_install_property (gobject_class, PROP_HW_SERIAL_NUMBER,
g_param_spec_string ("hw-serial-number", "Hardware serial number", g_param_spec_string ("hw-serial-number", "Hardware serial number",
"The serial number (hardware ID) of the Decklink card", "The serial number (hardware ID) of the Decklink card",
@ -188,6 +208,8 @@ gst_decklink_audio_sink_init (GstDecklinkAudioSink * self)
DEFAULT_DISCONT_WAIT); DEFAULT_DISCONT_WAIT);
self->buffer_time = DEFAULT_BUFFER_TIME * 1000; 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); 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; self->buffer_time = g_value_get_uint64 (value) * 1000;
GST_OBJECT_UNLOCK (self); GST_OBJECT_UNLOCK (self);
break; break;
case PROP_PERSISTENT_ID:
self->persistent_id = g_value_get_int64 (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; 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); g_value_set_uint64 (value, self->buffer_time / 1000);
GST_OBJECT_UNLOCK (self); GST_OBJECT_UNLOCK (self);
break; break;
case PROP_PERSISTENT_ID:
g_value_set_int64 (value, self->persistent_id);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -799,7 +827,7 @@ gst_decklink_audio_sink_open (GstBaseSink * bsink)
GST_DEBUG_OBJECT (self, "Starting"); GST_DEBUG_OBJECT (self, "Starting");
self->output = 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); GST_ELEMENT_CAST (self), TRUE);
if (!self->output) { if (!self->output) {
GST_ERROR_OBJECT (self, "Failed to acquire 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); g_mutex_unlock (&self->output->lock);
self->output->output->DisableAudioOutput (); 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); GST_ELEMENT_CAST (self), TRUE);
self->output = NULL; self->output = NULL;
} }

View file

@ -58,6 +58,8 @@ struct _GstDecklinkAudioSink
GstAudioStreamAlign *stream_align; GstAudioStreamAlign *stream_align;
GstAudioResampler *resampler; GstAudioResampler *resampler;
guint resampler_in_rate, resampler_out_rate; guint resampler_in_rate, resampler_out_rate;
gint64 persistent_id;
}; };
struct _GstDecklinkAudioSinkClass struct _GstDecklinkAudioSinkClass

View file

@ -57,6 +57,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_audio_src_debug);
#define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) ) #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
#endif #endif
#define DEFAULT_PERSISTENT_ID (-1)
enum enum
{ {
PROP_0, PROP_0,
@ -66,7 +68,8 @@ enum
PROP_DISCONT_WAIT, PROP_DISCONT_WAIT,
PROP_BUFFER_SIZE, PROP_BUFFER_SIZE,
PROP_CHANNELS, PROP_CHANNELS,
PROP_HW_SERIAL_NUMBER PROP_HW_SERIAL_NUMBER,
PROP_PERSISTENT_ID
}; };
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("src", 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 | (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT))); 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_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD,
g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold",
"Timestamp alignment threshold in nanoseconds", 0, "Timestamp alignment threshold in nanoseconds", 0,
@ -246,6 +266,8 @@ gst_decklink_audio_src_init (GstDecklinkAudioSrc * self)
self->skipped_last = 0; self->skipped_last = 0;
self->skip_from_timestamp = GST_CLOCK_TIME_NONE; self->skip_from_timestamp = GST_CLOCK_TIME_NONE;
self->skip_to_timestamp = GST_CLOCK_TIME_NONE; self->skip_to_timestamp = GST_CLOCK_TIME_NONE;
self->persistent_id = DEFAULT_PERSISTENT_ID;
} }
void void
@ -274,6 +296,9 @@ gst_decklink_audio_src_set_property (GObject * object, guint property_id,
case PROP_CHANNELS: case PROP_CHANNELS:
self->channels = (GstDecklinkAudioChannelsEnum) g_value_get_enum (value); self->channels = (GstDecklinkAudioChannelsEnum) g_value_get_enum (value);
break; break;
case PROP_PERSISTENT_ID:
self->persistent_id = g_value_get_int64 (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -311,6 +336,9 @@ gst_decklink_audio_src_get_property (GObject * object, guint property_id,
else else
g_value_set_string (value, NULL); g_value_set_string (value, NULL);
break; break;
case PROP_PERSISTENT_ID:
g_value_set_int64 (value, self->persistent_id);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -939,7 +967,7 @@ gst_decklink_audio_src_open (GstDecklinkAudioSrc * self)
GST_DEBUG_OBJECT (self, "Opening"); GST_DEBUG_OBJECT (self, "Opening");
self->input = 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); GST_ELEMENT_CAST (self), TRUE);
if (!self->input) { if (!self->input) {
GST_ERROR_OBJECT (self, "Failed to acquire 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; self->input->got_audio_packet = NULL;
g_mutex_unlock (&self->input->lock); 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); GST_ELEMENT_CAST (self), TRUE);
self->input = NULL; self->input = NULL;
} }

View file

@ -52,6 +52,7 @@ struct _GstDecklinkAudioSrc
GstDecklinkModeEnum mode; GstDecklinkModeEnum mode;
GstDecklinkAudioConnectionEnum connection; GstDecklinkAudioConnectionEnum connection;
gint device_number; gint device_number;
gint64 persistent_id;
GstDecklinkAudioChannelsEnum channels; GstDecklinkAudioChannelsEnum channels;
gint64 channels_found; gint64 channels_found;

View file

@ -24,6 +24,8 @@
#include "gstdecklinkdeviceprovider.h" #include "gstdecklinkdeviceprovider.h"
#include "gstdecklink.h" #include "gstdecklink.h"
#define DEFAULT_PERSISTENT_ID (-1)
G_DEFINE_TYPE (GstDecklinkDeviceProvider, gst_decklink_device_provider, G_DEFINE_TYPE (GstDecklinkDeviceProvider, gst_decklink_device_provider,
GST_TYPE_DEVICE_PROVIDER); GST_TYPE_DEVICE_PROVIDER);
GST_DEVICE_PROVIDER_REGISTER_DEFINE (decklinkdeviceprovider, "decklinkdeviceprovider", GST_DEVICE_PROVIDER_REGISTER_DEFINE (decklinkdeviceprovider, "decklinkdeviceprovider",
@ -77,7 +79,7 @@ gst_decklink_device_create_element (GstDevice * device, const gchar * name)
} }
if (ret) { if (ret) {
g_object_set (ret, "device-number", self->device_number, NULL); g_object_set (ret, "persistent-id", self->persistent_id, NULL);
} }
return ret; return ret;

View file

@ -140,6 +140,8 @@
GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_sink_debug); GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_sink_debug);
#define GST_CAT_DEFAULT gst_decklink_video_sink_debug #define GST_CAT_DEFAULT gst_decklink_video_sink_debug
#define DEFAULT_PERSISTENT_ID (-1)
class GStreamerVideoOutputCallback:public IDeckLinkVideoOutputCallback class GStreamerVideoOutputCallback:public IDeckLinkVideoOutputCallback
{ {
public: public:
@ -521,6 +523,7 @@ enum
PROP_CC_LINE, PROP_CC_LINE,
PROP_AFD_BAR_LINE, PROP_AFD_BAR_LINE,
PROP_MAPPING_FORMAT, PROP_MAPPING_FORMAT,
PROP_PERSISTENT_ID
}; };
static void gst_decklink_video_sink_set_property (GObject * object, 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, "Output device instance to use", 0, G_MAXINT, 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT))); 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_object_class_install_property (gobject_class, PROP_VIDEO_FORMAT,
g_param_spec_enum ("video-format", "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->mode = GST_DECKLINK_MODE_NTSC;
self->device_number = 0; self->device_number = 0;
self->persistent_id = DEFAULT_PERSISTENT_ID;
self->video_format = GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV; self->video_format = GST_DECKLINK_VIDEO_FORMAT_8BIT_YUV;
self->profile_id = GST_DECKLINK_PROFILE_ID_DEFAULT; self->profile_id = GST_DECKLINK_PROFILE_ID_DEFAULT;
/* VITC is legacy, we should expect RP188 in modern use cases */ /* 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 = self->mapping_format =
(GstDecklinkMappingFormat) g_value_get_enum (value); (GstDecklinkMappingFormat) g_value_get_enum (value);
break; break;
case PROP_PERSISTENT_ID:
self->persistent_id = g_value_get_int64 (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -840,6 +863,9 @@ gst_decklink_video_sink_get_property (GObject * object, guint property_id,
case PROP_MAPPING_FORMAT: case PROP_MAPPING_FORMAT:
g_value_set_enum (value, self->mapping_format); g_value_set_enum (value, self->mapping_format);
break; break;
case PROP_PERSISTENT_ID:
g_value_set_int64 (value, self->persistent_id);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break; break;
@ -1673,7 +1699,7 @@ gst_decklink_video_sink_open (GstBaseSink * bsink)
GST_DEBUG_OBJECT (self, "Starting"); GST_DEBUG_OBJECT (self, "Starting");
self->output = 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); GST_ELEMENT_CAST (self), FALSE);
if (!self->output) { if (!self->output) {
GST_ERROR_OBJECT (self, "Failed to acquire 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); g_mutex_unlock (&self->output->lock);
self->output->output->DisableVideoOutput (); 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); GST_ELEMENT_CAST (self), FALSE);
self->output = NULL; self->output = NULL;
} }

View file

@ -51,6 +51,7 @@ struct _GstDecklinkVideoSink
GstDecklinkModeEnum mode; GstDecklinkModeEnum mode;
gint device_number; gint device_number;
gint64 persistent_id;
GstDecklinkVideoFormat video_format; GstDecklinkVideoFormat video_format;
GstDecklinkProfileId profile_id; GstDecklinkProfileId profile_id;
BMDTimecodeFormat timecode_format; BMDTimecodeFormat timecode_format;

View file

@ -149,6 +149,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug);
#define DEFAULT_DROP_NO_SIGNAL_FRAMES (FALSE) #define DEFAULT_DROP_NO_SIGNAL_FRAMES (FALSE)
#define DEFAULT_OUTPUT_CC (FALSE) #define DEFAULT_OUTPUT_CC (FALSE)
#define DEFAULT_OUTPUT_AFD_BAR (FALSE) #define DEFAULT_OUTPUT_AFD_BAR (FALSE)
#define DEFAULT_PERSISTENT_ID (-1)
#ifndef ABSDIFF #ifndef ABSDIFF
#define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) ) #define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
@ -169,6 +170,7 @@ enum
PROP_DROP_NO_SIGNAL_FRAMES, PROP_DROP_NO_SIGNAL_FRAMES,
PROP_SIGNAL, PROP_SIGNAL,
PROP_HW_SERIAL_NUMBER, PROP_HW_SERIAL_NUMBER,
PROP_PERSISTENT_ID,
PROP_OUTPUT_CC, PROP_OUTPUT_CC,
PROP_OUTPUT_AFD_BAR, PROP_OUTPUT_AFD_BAR,
}; };
@ -290,6 +292,23 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT))); 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_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
g_param_spec_uint ("buffer-size", "Buffer Size", g_param_spec_uint ("buffer-size", "Buffer Size",
"Size of internal buffer in number of video frames", 1, "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->caps_format = bmdFormat8BitYUV;
self->connection = DEFAULT_CONNECTION; self->connection = DEFAULT_CONNECTION;
self->device_number = 0; self->device_number = 0;
self->persistent_id = DEFAULT_PERSISTENT_ID;
self->buffer_size = DEFAULT_BUFFER_SIZE; self->buffer_size = DEFAULT_BUFFER_SIZE;
self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO; self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO;
self->profile_id = GST_DECKLINK_PROFILE_ID_DEFAULT; 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: case PROP_DROP_NO_SIGNAL_FRAMES:
self->drop_no_signal_frames = g_value_get_boolean (value); self->drop_no_signal_frames = g_value_get_boolean (value);
break; break;
case PROP_PERSISTENT_ID:
self->persistent_id = g_value_get_int64 (value);
break;
case PROP_OUTPUT_CC: case PROP_OUTPUT_CC:
self->output_cc = g_value_get_boolean (value); self->output_cc = g_value_get_boolean (value);
break; break;
@ -540,6 +563,9 @@ gst_decklink_video_src_get_property (GObject * object, guint property_id,
case PROP_DROP_NO_SIGNAL_FRAMES: case PROP_DROP_NO_SIGNAL_FRAMES:
g_value_set_boolean (value, self->drop_no_signal_frames); g_value_set_boolean (value, self->drop_no_signal_frames);
break; break;
case PROP_PERSISTENT_ID:
g_value_set_int64 (value, self->persistent_id);
break;
case PROP_SIGNAL: case PROP_SIGNAL:
g_value_set_boolean (value, self->signal_state == SIGNAL_STATE_AVAILABLE); g_value_set_boolean (value, self->signal_state == SIGNAL_STATE_AVAILABLE);
break; break;
@ -1523,7 +1549,7 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
GST_DEBUG_OBJECT (self, "Opening"); GST_DEBUG_OBJECT (self, "Opening");
self->input = 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); GST_ELEMENT_CAST (self), FALSE);
if (!self->input) { if (!self->input) {
GST_ERROR_OBJECT (self, "Failed to acquire 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; self->input->start_streams = NULL;
g_mutex_unlock (&self->input->lock); 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); GST_ELEMENT_CAST (self), FALSE);
self->input = NULL; self->input = NULL;
} }

View file

@ -62,6 +62,7 @@ struct _GstDecklinkVideoSrc
BMDPixelFormat caps_format; BMDPixelFormat caps_format;
GstDecklinkConnectionEnum connection; GstDecklinkConnectionEnum connection;
gint device_number; gint device_number;
gint64 persistent_id;
gboolean output_stream_time; gboolean output_stream_time;
GstClockTime skip_first_time; GstClockTime skip_first_time;
gboolean drop_no_signal_frames; gboolean drop_no_signal_frames;