"type": "gboolean",
"writable": true
},
+ "port-names": {
+ "blurb": "Comma-separated list of port name including \"client_name:\" prefix",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": false,
+ "controllable": false,
+ "default": "NULL",
+ "mutable": "ready",
+ "readable": true,
+ "type": "gchararray",
+ "writable": true
+ },
"port-pattern": {
"blurb": "A pattern to select which ports to connect to (NULL = first physical ports)",
"conditionally-available": false,
"type": "gboolean",
"writable": true
},
+ "port-names": {
+ "blurb": "Comma-separated list of port name including \"client_name:\" prefix",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": false,
+ "controllable": false,
+ "default": "NULL",
+ "mutable": "ready",
+ "readable": true,
+ "type": "gchararray",
+ "writable": true
+ },
"port-pattern": {
"blurb": "A pattern to select which ports to connect to (NULL = first physical ports)",
"conditionally-available": false,
"desc": "Automatically connect ports to as many physical ports as possible",
"name": "auto-forced",
"value": "2"
+ },
+ {
+ "desc": "Connect ports to explicitly requested physical ports",
+ "name": "explicit",
+ "value": "3"
}
]
},
{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);
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;
/**
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;
+}
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__ */
{
GstJackAudioSink *sink;
GstJackRingBuffer *abuf;
- const char **ports;
gint sample_rate, buffer_size;
gint i, rate, bpf, channels, res;
jack_client_t *client;
/* 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),
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"));
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:
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;
}
}
PROP_PORT_PATTERN,
PROP_TRANSPORT,
PROP_LOW_LATENCY,
+ PROP_PORT_NAMES,
PROP_LAST
};
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>");
sink->port_pattern = NULL;
}
+ g_clear_pointer (&sink->port_names, g_free);
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
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;
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;
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);
* 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);
/* 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 *
gchar *port_pattern;
guint transport;
gboolean low_latency;
+ gchar *port_names;
/* our client */
GstJackAudioClient *client;
{
GstJackAudioSrc *src;
GstJackRingBuffer *abuf;
- const char **ports;
gint sample_rate, buffer_size;
gint i, bpf, rate, channels, res;
jack_client_t *client;
/* 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),
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"));
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:
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;
}
}
PROP_PORT_PATTERN,
PROP_TRANSPORT,
PROP_LOW_LATENCY,
+ PROP_PORT_NAMES,
PROP_LAST
};
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,
src->port_pattern = NULL;
}
+ g_clear_pointer (&src->port_names, g_free);
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
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;
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;
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++);
* 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);
/* 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 *
gchar *port_pattern;
guint transport;
gboolean low_latency;
+ gchar *port_names;
/* our client */
GstJackAudioClient *client;