summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorSeungha Yang <seungha@centricular.com>2021-07-27 18:33:18 +0900
committerSeungha Yang <seungha@centricular.com>2021-07-30 15:58:20 +0900
commit4a5197dc27a3300111df5ee44a613a58ccdabd47 (patch)
tree6d91de4092f08ecd7e97f06327c64c494e617af6 /ext
parent4ed342db5d3da7baabad258b5530a1724c268e04 (diff)
downloadgstreamer-plugins-good-4a5197dc27a3300111df5ee44a613a58ccdabd47.tar.gz
jack: Add port-names property to select ports explicitly
By this new property, user can select physical port to connect, and element will pick requested port instead of random ones. User should provide full port name including "client_name:" prefix. An example is jackaudiosrc port-names="system:capture_1,system:capture_3" ! ... jackaudiosink port-names="system:playback_2" In addition to "port-names" property, a new connect type "explicit" is added so that element can post error message if requested "port-names" contains invalid port(s). Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/1037>
Diffstat (limited to 'ext')
-rw-r--r--ext/jack/gstjack.c3
-rw-r--r--ext/jack/gstjack.h12
-rw-r--r--ext/jack/gstjackaudioclient.c51
-rw-r--r--ext/jack/gstjackaudioclient.h4
-rw-r--r--ext/jack/gstjackaudiosink.c130
-rw-r--r--ext/jack/gstjackaudiosink.h1
-rw-r--r--ext/jack/gstjackaudiosrc.c127
-rw-r--r--ext/jack/gstjackaudiosrc.h1
8 files changed, 294 insertions, 35 deletions
diff --git a/ext/jack/gstjack.c b/ext/jack/gstjack.c
index 06c3ad043..59682bb7b 100644
--- a/ext/jack/gstjack.c
+++ b/ext/jack/gstjack.c
@@ -37,6 +37,9 @@ gst_jack_connect_get_type (void)
{GST_JACK_CONNECT_AUTO_FORCED,
"Automatically connect ports to as many physical ports as possible",
"auto-forced"},
+ {GST_JACK_CONNECT_EXPLICIT,
+ "Connect ports to explicitly requested physical ports",
+ "explicit"},
{0, NULL, NULL},
};
GType tmp = g_enum_register_static ("GstJackConnect", jack_connect_enums);
diff --git a/ext/jack/gstjack.h b/ext/jack/gstjack.h
index ad238fdb7..84693f614 100644
--- a/ext/jack/gstjack.h
+++ b/ext/jack/gstjack.h
@@ -45,7 +45,17 @@ GST_ELEMENT_REGISTER_DECLARE (jackaudiosink);
typedef enum {
GST_JACK_CONNECT_NONE,
GST_JACK_CONNECT_AUTO,
- GST_JACK_CONNECT_AUTO_FORCED
+ GST_JACK_CONNECT_AUTO_FORCED,
+
+ /**
+ * GstJackConnect::explicit
+ *
+ * In this mode, the element will try to connect to explicitly requested
+ * port specified by "port-names".
+ *
+ * Since: 1.20
+ */
+ GST_JACK_CONNECT_EXPLICIT,
} GstJackConnect;
/**
diff --git a/ext/jack/gstjackaudioclient.c b/ext/jack/gstjackaudioclient.c
index 3d0dbc194..5b483a810 100644
--- a/ext/jack/gstjackaudioclient.c
+++ b/ext/jack/gstjackaudioclient.c
@@ -635,3 +635,54 @@ gst_jack_audio_client_get_transport_state (GstJackAudioClient * client)
client->conn->transport_state = GST_STATE_VOID_PENDING;
return state;
}
+
+/**
+ * gst_jack_audio_client_get_port_names_from_string:
+ * @jclient: a jack_client_t handle
+ * @port_names: comma-separated jack port name(s)
+ * @port_flags: JackPortFlags
+ *
+ * Returns: a newly-allocated %NULL-terminated array of strings or %NULL
+ * if @port_names contains invalid port name. Use g_strfreev() to free it.
+ */
+gchar **
+gst_jack_audio_client_get_port_names_from_string (jack_client_t * jclient,
+ const gchar * port_names, gint port_flags)
+{
+ gchar **p = NULL;
+ guint i, len;
+
+ g_return_val_if_fail (jclient != NULL, NULL);
+
+ if (!port_names)
+ return NULL;
+
+ p = g_strsplit (port_names, ",", 0);
+ len = g_strv_length (p);
+
+ if (len < 1)
+ goto invalid;
+
+ for (i = 0; i < len; i++) {
+ jack_port_t *port = jack_port_by_name (jclient, p[i]);
+ int flags;
+
+ if (!port) {
+ GST_WARNING ("Couldn't get jack port by name %s", p[i]);
+ goto invalid;
+ }
+
+ flags = jack_port_flags (port);
+ if ((flags & port_flags) != port_flags) {
+ GST_WARNING ("Port flags 0x%x doesn't match expected flags 0x%x",
+ flags, port_flags);
+ goto invalid;
+ }
+ }
+
+ return p;
+
+invalid:
+ g_strfreev (p);
+ return NULL;
+}
diff --git a/ext/jack/gstjackaudioclient.h b/ext/jack/gstjackaudioclient.h
index bf4d93e52..455edd2e7 100644
--- a/ext/jack/gstjackaudioclient.h
+++ b/ext/jack/gstjackaudioclient.h
@@ -56,6 +56,10 @@ gboolean gst_jack_audio_client_set_active (GstJackAudioClient *
GstState gst_jack_audio_client_get_transport_state (GstJackAudioClient *client);
+gchar ** gst_jack_audio_client_get_port_names_from_string (jack_client_t *jclient,
+ const gchar *port_names,
+ gint port_flags);
+
G_END_DECLS
#endif /* __GST_JACK_AUDIO_CLIENT_H__ */
diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c
index ccdca5a54..4d18f7a36 100644
--- a/ext/jack/gstjackaudiosink.c
+++ b/ext/jack/gstjackaudiosink.c
@@ -401,7 +401,6 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
{
GstJackAudioSink *sink;
GstJackRingBuffer *abuf;
- const char **ports;
gint sample_rate, buffer_size;
gint i, rate, bpf, channels, res;
jack_client_t *client;
@@ -459,18 +458,39 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
/* if we need to automatically connect the ports, do so now. We must do this
* after activating the client. */
if (sink->connect == GST_JACK_CONNECT_AUTO
- || sink->connect == GST_JACK_CONNECT_AUTO_FORCED) {
+ || sink->connect == GST_JACK_CONNECT_AUTO_FORCED
+ || sink->connect == GST_JACK_CONNECT_EXPLICIT) {
+ const char **available_ports = NULL;
+ const char **jack_ports = NULL;
+ char **user_ports = NULL;
+
/* find all the physical input ports. A physical input port is a port
* associated with a hardware device. Someone needs connect to a physical
* port in order to hear something. */
- if (sink->port_pattern == NULL) {
- ports = jack_get_ports (client, NULL, NULL,
- JackPortIsPhysical | JackPortIsInput);
- } else {
- ports = jack_get_ports (client, sink->port_pattern, NULL,
- JackPortIsInput);
+ if (sink->port_names) {
+ user_ports = gst_jack_audio_client_get_port_names_from_string (client,
+ sink->port_names, JackPortIsInput);
+
+ if (user_ports)
+ available_ports = (const char **) user_ports;
}
- if (ports == NULL) {
+
+ if (!available_ports && sink->connect == GST_JACK_CONNECT_EXPLICIT)
+ goto wrong_port_names;
+
+ if (!available_ports) {
+ if (!sink->port_pattern) {
+ jack_ports = jack_get_ports (client, NULL, NULL,
+ JackPortIsPhysical | JackPortIsInput);
+ } else {
+ jack_ports = jack_get_ports (client, sink->port_pattern, NULL,
+ JackPortIsInput);
+ }
+
+ available_ports = jack_ports;
+ }
+
+ if (!available_ports) {
/* no ports? fine then we don't do anything except for posting a warning
* message. */
GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL),
@@ -480,7 +500,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
for (i = 0; i < channels; i++) {
/* stop when all input ports are exhausted */
- if (ports[i] == NULL) {
+ if (!available_ports[i]) {
/* post a warning that we could not connect all ports */
GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL),
("No more physical ports, leaving some ports unconnected"));
@@ -489,11 +509,18 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
GST_DEBUG_OBJECT (sink, "try connecting to %s",
jack_port_name (sink->ports[i]));
/* connect the port to a physical port */
- res = jack_connect (client, jack_port_name (sink->ports[i]), ports[i]);
- if (res != 0 && res != EEXIST)
+ res = jack_connect (client,
+ jack_port_name (sink->ports[i]), available_ports[i]);
+ if (res != 0 && res != EEXIST) {
+ jack_free (jack_ports);
+ g_strfreev (user_ports);
+
goto cannot_connect;
+ }
}
- jack_free (ports);
+
+ jack_free (jack_ports);
+ g_strfreev (user_ports);
}
done:
@@ -528,7 +555,12 @@ cannot_connect:
GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL),
("Could not connect output ports to physical ports (%d:%s)",
res, g_strerror (res)));
- jack_free (ports);
+ return FALSE;
+ }
+wrong_port_names:
+ {
+ GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL),
+ ("Invalid port-names was provided"));
return FALSE;
}
}
@@ -699,6 +731,7 @@ enum
PROP_PORT_PATTERN,
PROP_TRANSPORT,
PROP_LOW_LATENCY,
+ PROP_PORT_NAMES,
PROP_LAST
};
@@ -807,6 +840,19 @@ gst_jack_audio_sink_class_init (GstJackAudioSinkClass * klass)
GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ /**
+ * GstJackAudioSink:port-names:
+ *
+ * Comma-separated list of port name including "client_name:" prefix
+ *
+ * Since: 1.20
+ */
+ g_object_class_install_property (gobject_class, PROP_PORT_NAMES,
+ g_param_spec_string ("port-names", "Port Names",
+ "Comma-separated list of port name including \"client_name:\" prefix",
+ NULL, GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
gst_element_class_set_static_metadata (gstelement_class, "Audio Sink (Jack)",
"Sink/Audio", "Output audio to a JACK server",
"Wim Taymans <wim.taymans@gmail.com>");
@@ -857,6 +903,8 @@ gst_jack_audio_sink_dispose (GObject * object)
sink->port_pattern = NULL;
}
+ g_clear_pointer (&sink->port_names, g_free);
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -896,6 +944,10 @@ gst_jack_audio_sink_set_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY:
sink->low_latency = g_value_get_boolean (value);
break;
+ case PROP_PORT_NAMES:
+ g_free (sink->port_names);
+ sink->port_names = g_value_dup_string (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -932,6 +984,9 @@ gst_jack_audio_sink_get_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY:
g_value_set_boolean (value, sink->low_latency);
break;
+ case PROP_PORT_NAMES:
+ g_value_set_string (value, sink->port_names);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -950,14 +1005,42 @@ gst_jack_audio_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
if (sink->client == NULL)
goto no_client;
+ if (sink->connect == GST_JACK_CONNECT_EXPLICIT && !sink->port_names)
+ goto no_port_names;
+
client = gst_jack_audio_client_get_client (sink->client);
- if (sink->connect == GST_JACK_CONNECT_AUTO) {
+ if (sink->connect == GST_JACK_CONNECT_AUTO ||
+ sink->connect == GST_JACK_CONNECT_EXPLICIT) {
+ max = 0;
+
+ if (sink->port_names) {
+ gchar **user_ports =
+ gst_jack_audio_client_get_port_names_from_string (client,
+ sink->port_names, JackPortIsInput);
+
+ if (user_ports) {
+ max = g_strv_length (user_ports);
+ } else {
+ GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND,
+ ("Invalid \"port-names\" was requested"),
+ ("Requested \"port-names\" %s contains invalid name",
+ sink->port_names));
+ }
+
+ g_strfreev (user_ports);
+ }
+
+ if (max > 0)
+ goto found;
+
+ if (sink->connect == GST_JACK_CONNECT_EXPLICIT)
+ goto no_port_names;
+
/* get a port count, this is the number of channels we can automatically
* connect. */
ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical | JackPortIsInput);
- max = 0;
if (ports != NULL) {
for (; ports[max]; max++);
jack_free (ports);
@@ -968,7 +1051,13 @@ gst_jack_audio_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
* pads. */
max = G_MAXINT;
}
- min = MIN (1, max);
+
+found:
+ if (sink->connect == GST_JACK_CONNECT_EXPLICIT) {
+ min = max;
+ } else {
+ min = MIN (1, max);
+ }
rate = jack_get_sample_rate (client);
@@ -996,6 +1085,13 @@ no_client:
/* base class will get template caps for us when we return NULL */
return NULL;
}
+no_port_names:
+ {
+ GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS,
+ ("User must provide valid port names"),
+ ("\"port-names\" contains invalid name or NULL string"));
+ return NULL;
+ }
}
static GstAudioRingBuffer *
diff --git a/ext/jack/gstjackaudiosink.h b/ext/jack/gstjackaudiosink.h
index ffba82431..088289de4 100644
--- a/ext/jack/gstjackaudiosink.h
+++ b/ext/jack/gstjackaudiosink.h
@@ -56,6 +56,7 @@ struct _GstJackAudioSink {
gchar *port_pattern;
guint transport;
gboolean low_latency;
+ gchar *port_names;
/* our client */
GstJackAudioClient *client;
diff --git a/ext/jack/gstjackaudiosrc.c b/ext/jack/gstjackaudiosrc.c
index 11f4f9157..40784fa4a 100644
--- a/ext/jack/gstjackaudiosrc.c
+++ b/ext/jack/gstjackaudiosrc.c
@@ -407,7 +407,6 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
{
GstJackAudioSrc *src;
GstJackRingBuffer *abuf;
- const char **ports;
gint sample_rate, buffer_size;
gint i, bpf, rate, channels, res;
jack_client_t *client;
@@ -467,20 +466,38 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
/* if we need to automatically connect the ports, do so now. We must do this
* after activating the client. */
if (src->connect == GST_JACK_CONNECT_AUTO
- || src->connect == GST_JACK_CONNECT_AUTO_FORCED) {
+ || src->connect == GST_JACK_CONNECT_AUTO_FORCED
+ || src->connect == GST_JACK_CONNECT_EXPLICIT) {
+ const char **available_ports = NULL;
+ const char **jack_ports = NULL;
+ char **user_ports = NULL;
+
/* find all the physical output ports. A physical output port is a port
* associated with a hardware device. Someone needs connect to a physical
* port in order to capture something. */
- if (src->port_pattern == NULL) {
- ports = jack_get_ports (client, NULL, NULL,
- JackPortIsPhysical | JackPortIsOutput);
- } else {
- ports = jack_get_ports (client, src->port_pattern, NULL,
- JackPortIsOutput);
+ if (src->port_names) {
+ user_ports = gst_jack_audio_client_get_port_names_from_string (client,
+ src->port_names, JackPortIsOutput);
+
+ if (user_ports)
+ available_ports = (const char **) user_ports;
}
- if (ports == NULL) {
+ if (!available_ports && src->connect == GST_JACK_CONNECT_EXPLICIT)
+ goto wrong_port_names;
+
+ if (!available_ports) {
+ if (!src->port_pattern) {
+ jack_ports = jack_get_ports (client, NULL, NULL,
+ JackPortIsPhysical | JackPortIsOutput);
+ } else {
+ jack_ports = jack_get_ports (client, src->port_pattern, NULL,
+ JackPortIsOutput);
+ }
+ }
+
+ if (!available_ports) {
/* no ports? fine then we don't do anything except for posting a warning
* message. */
GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND, (NULL),
@@ -490,7 +507,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
for (i = 0; i < channels; i++) {
/* stop when all output ports are exhausted */
- if (ports[i] == NULL) {
+ if (!available_ports[i]) {
/* post a warning that we could not connect all ports */
GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND, (NULL),
("No more physical ports, leaving some ports unconnected"));
@@ -500,11 +517,18 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf,
jack_port_name (src->ports[i]));
/* connect the physical port to a port */
- res = jack_connect (client, ports[i], jack_port_name (src->ports[i]));
- if (res != 0 && res != EEXIST)
+ res = jack_connect (client,
+ available_ports[i], jack_port_name (src->ports[i]));
+ if (res != 0 && res != EEXIST) {
+ jack_free (jack_ports);
+ g_strfreev (user_ports);
+
goto cannot_connect;
+ }
}
- jack_free (ports);
+
+ jack_free (jack_ports);
+ g_strfreev (user_ports);
}
done:
@@ -539,7 +563,12 @@ cannot_connect:
GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
("Could not connect input ports to physical ports (%d:%s)",
res, g_strerror (res)));
- jack_free (ports);
+ return FALSE;
+ }
+wrong_port_names:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
+ ("Invalid port-names was provided"));
return FALSE;
}
}
@@ -701,6 +730,7 @@ enum
PROP_PORT_PATTERN,
PROP_TRANSPORT,
PROP_LOW_LATENCY,
+ PROP_PORT_NAMES,
PROP_LAST
};
@@ -825,6 +855,19 @@ gst_jack_audio_src_class_init (GstJackAudioSrcClass * klass)
GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ /**
+ * GstJackAudioSrc:port-names:
+ *
+ * Comma-separated list of port name including "client_name:" prefix
+ *
+ * Since: 1.20
+ */
+ g_object_class_install_property (gobject_class, PROP_PORT_NAMES,
+ g_param_spec_string ("port-names", "Port Names",
+ "Comma-separated list of port name including \"client_name:\" prefix",
+ NULL, GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
gst_element_class_set_static_metadata (gstelement_class,
@@ -875,6 +918,8 @@ gst_jack_audio_src_dispose (GObject * object)
src->port_pattern = NULL;
}
+ g_clear_pointer (&src->port_names, g_free);
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -912,6 +957,10 @@ gst_jack_audio_src_set_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY:
src->low_latency = g_value_get_boolean (value);
break;
+ case PROP_PORT_NAMES:
+ g_free (src->port_names);
+ src->port_names = g_value_dup_string (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -946,6 +995,9 @@ gst_jack_audio_src_get_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY:
g_value_set_boolean (value, src->low_latency);
break;
+ case PROP_PORT_NAMES:
+ g_value_set_string (value, src->port_names);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -964,14 +1016,42 @@ gst_jack_audio_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
if (src->client == NULL)
goto no_client;
+ if (src->connect == GST_JACK_CONNECT_EXPLICIT && !src->port_names)
+ goto no_port_names;
+
client = gst_jack_audio_client_get_client (src->client);
- if (src->connect == GST_JACK_CONNECT_AUTO) {
+ if (src->connect == GST_JACK_CONNECT_AUTO ||
+ src->connect == GST_JACK_CONNECT_EXPLICIT) {
+ max = 0;
+
+ if (src->port_names) {
+ gchar **user_ports =
+ gst_jack_audio_client_get_port_names_from_string (client,
+ src->port_names, JackPortIsOutput);
+
+ if (user_ports) {
+ max = g_strv_length (user_ports);
+ } else {
+ GST_ELEMENT_WARNING (src, RESOURCE, NOT_FOUND,
+ ("Invalid \"port-names\" was requested"),
+ ("Requested \"port-names\" %s contains invalid name",
+ src->port_names));
+ }
+
+ g_strfreev (user_ports);
+ }
+
+ if (max > 0)
+ goto found;
+
+ if (src->connect == GST_JACK_CONNECT_EXPLICIT)
+ goto no_port_names;
+
/* get a port count, this is the number of channels we can automatically
* connect. */
ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical | JackPortIsOutput);
- max = 0;
if (ports != NULL) {
for (; ports[max]; max++);
@@ -983,7 +1063,13 @@ gst_jack_audio_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
* pads. */
max = G_MAXINT;
}
- min = MIN (1, max);
+
+found:
+ if (src->connect == GST_JACK_CONNECT_EXPLICIT) {
+ min = max;
+ } else {
+ min = MIN (1, max);
+ }
rate = jack_get_sample_rate (client);
@@ -1011,6 +1097,13 @@ no_client:
/* base class will get template caps for us when we return NULL */
return NULL;
}
+no_port_names:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS,
+ ("User must provide valid port names"),
+ ("\"port-names\" contains invalid name or NULL string"));
+ return NULL;
+ }
}
static GstAudioRingBuffer *
diff --git a/ext/jack/gstjackaudiosrc.h b/ext/jack/gstjackaudiosrc.h
index d6b08b982..3657c60ce 100644
--- a/ext/jack/gstjackaudiosrc.h
+++ b/ext/jack/gstjackaudiosrc.h
@@ -73,6 +73,7 @@ struct _GstJackAudioSrc
gchar *port_pattern;
guint transport;
gboolean low_latency;
+ gchar *port_names;
/* our client */
GstJackAudioClient *client;