From ba4ef9b5d99c446bd836c9a6dae2262f67aa2932 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 6 May 2016 13:01:52 +0200 Subject: [PATCH] Introduce the concept of a Port A port is an input or output on a Node. Channels are created from the ports and inherit the direction of the port. do automatic port selection based on the direction and caps and node/port name. Simplify stream_connect by passing the direction. Fix pinossink to connect in setcaps so that we know the format and can select a good sink to connect to. --- pinos/Makefile.am | 5 +- pinos/client/buffer.h | 6 +- pinos/client/context.c | 16 +- pinos/client/introspect.c | 163 ++--- pinos/client/introspect.h | 301 ++++---- pinos/client/pinos.h | 1 + pinos/client/private.h | 4 +- pinos/client/stream.c | 94 +-- pinos/client/stream.h | 9 +- pinos/client/subscribe.c | 8 +- pinos/client/subscribe.h | 4 +- pinos/dbus/org.pinos.xml | 137 ++-- pinos/gst/gstpinosdeviceprovider.c | 64 +- pinos/gst/gstpinossink.c | 55 +- pinos/gst/gstpinossrc.c | 2 +- pinos/modules/gst/gst-manager.c | 26 +- pinos/modules/gst/gst-sink.c | 212 ++---- pinos/modules/gst/gst-sink.h | 7 +- pinos/modules/gst/gst-source.c | 232 ++---- pinos/modules/gst/gst-source.h | 7 +- pinos/server/channel.c | 62 +- pinos/server/client-source.h | 77 -- pinos/server/client.c | 192 ++--- pinos/server/daemon.c | 74 +- pinos/server/daemon.h | 5 +- pinos/server/node.c | 304 ++++++-- pinos/server/node.h | 27 +- pinos/server/port.c | 666 ++++++++++++++++++ pinos/server/port.h | 101 +++ pinos/server/sink.c | 646 ----------------- pinos/server/sink.h | 109 --- .../server/{client-source.c => upload-node.c} | 313 ++++---- pinos/server/upload-node.h | 75 ++ pinos/tests/test-client.c | 2 +- pinos/tools/pinos-monitor.c | 53 +- 35 files changed, 1939 insertions(+), 2120 deletions(-) delete mode 100644 pinos/server/client-source.h create mode 100644 pinos/server/port.c create mode 100644 pinos/server/port.h delete mode 100644 pinos/server/sink.c delete mode 100644 pinos/server/sink.h rename pinos/server/{client-source.c => upload-node.c} (51%) create mode 100644 pinos/server/upload-node.h diff --git a/pinos/Makefile.am b/pinos/Makefile.am index c650e1bc1..4f989b17d 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -206,9 +206,8 @@ libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \ server/client.c server/client.h \ server/daemon.c server/daemon.h \ server/node.c server/node.h \ - server/source.c server/source.h \ - server/sink.c server/sink.h \ - server/client-source.c server/client-source.h \ + server/port.c server/port.h \ + server/upload-node.c server/upload-node.h \ server/channel.c server/channel.h \ modules/gst/gst-manager.c modules/gst/gst-manager.h \ modules/gst/gst-source.c modules/gst/gst-source.h \ diff --git a/pinos/client/buffer.h b/pinos/client/buffer.h index 7b8d91974..8e4dffaa9 100644 --- a/pinos/client/buffer.h +++ b/pinos/client/buffer.h @@ -189,7 +189,7 @@ gboolean pinos_buffer_builder_add_release_fd_payload (PinosBufferBuil */ typedef struct { guint8 id; - gchar *format; + const gchar *format; } PinosPacketFormatChange; gboolean pinos_buffer_iter_parse_format_change (PinosBufferIter *iter, @@ -207,8 +207,8 @@ gboolean pinos_buffer_builder_add_format_change (PinosBufferBuilder * A new property change. */ typedef struct { - gchar *key; - gchar *value; + const gchar *key; + const gchar *value; } PinosPacketPropertyChange; gboolean pinos_buffer_iter_parse_property_change (PinosBufferIter *iter, diff --git a/pinos/client/context.c b/pinos/client/context.c index fbb846e75..1d7c22864 100644 --- a/pinos/client/context.c +++ b/pinos/client/context.c @@ -147,8 +147,8 @@ pinos_context_finalize (GObject * object) if (priv->properties) pinos_properties_free (priv->properties); - g_list_free (priv->sources); - g_list_free (priv->sinks); + g_list_free (priv->nodes); + g_list_free (priv->ports); g_list_free (priv->clients); g_list_free (priv->channels); g_clear_object (&priv->subscribe); @@ -488,18 +488,18 @@ subscription_cb (PinosSubscribe *subscribe, } break; - case PINOS_SUBSCRIPTION_FLAG_SOURCE: + case PINOS_SUBSCRIPTION_FLAG_NODE: if (event == PINOS_SUBSCRIPTION_EVENT_NEW) - priv->sources = g_list_prepend (priv->sources, object); + priv->nodes = g_list_prepend (priv->nodes, object); else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) - priv->sources = g_list_remove (priv->sources, object); + priv->nodes = g_list_remove (priv->nodes, object); break; - case PINOS_SUBSCRIPTION_FLAG_SINK: + case PINOS_SUBSCRIPTION_FLAG_PORT: if (event == PINOS_SUBSCRIPTION_EVENT_NEW) - priv->sinks = g_list_prepend (priv->sinks, object); + priv->ports = g_list_prepend (priv->ports, object); else if (event == PINOS_SUBSCRIPTION_EVENT_REMOVE) - priv->sinks = g_list_remove (priv->sinks, object); + priv->ports = g_list_remove (priv->ports, object); break; case PINOS_SUBSCRIPTION_FLAG_CHANNEL: diff --git a/pinos/client/introspect.c b/pinos/client/introspect.c index c36e56c6f..b61942385 100644 --- a/pinos/client/introspect.c +++ b/pinos/client/introspect.c @@ -274,69 +274,66 @@ pinos_context_get_client_info_by_id (PinosContext *context, } /** - * pinos_source_state_as_string: - * @state: a #PinosSourceState + * pinos_node_state_as_string: + * @state: a #PinosNodeeState * * Return the string representation of @state. * * Returns: the string representation of @state. */ const gchar * -pinos_source_state_as_string (PinosSourceState state) +pinos_node_state_as_string (PinosNodeState state) { GEnumValue *val; - val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_SOURCE_STATE)), + val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_NODE_STATE)), state); return val == NULL ? "invalid-state" : val->value_nick; } static void -source_fill_info (PinosSourceInfo *info, GDBusProxy *proxy) +node_fill_info (PinosNodeInfo *info, GDBusProxy *proxy) { GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties"); info->id = proxy; - info->source_path = g_dbus_proxy_get_object_path (proxy); + info->node_path = g_dbus_proxy_get_object_path (proxy); info->change_mask = 0; SET_STRING ("Name", name, 0); SET_PROPERTIES ("Properties", properties, 1); - SET_UINT32 ("State", state, 2, PINOS_SOURCE_STATE_ERROR); - SET_BYTES ("PossibleFormats", possible_formats, 3); + SET_UINT32 ("State", state, 2, PINOS_NODE_STATE_ERROR); if (changed) g_hash_table_remove_all (changed); } static void -source_clear_info (PinosSourceInfo *info) +node_clear_info (PinosNodeInfo *info) { if (info->properties) pinos_properties_free (info->properties); - if (info->possible_formats) - g_bytes_unref (info->possible_formats); } /** - * pinos_context_list_source_info: + * pinos_context_list_node_info: * @context: a connected #PinosContext - * @flags: extra #PinosSourceInfoFlags - * @cb: a #PinosSourceInfoCallback + * @flags: extra #PinosNodeInfoFlags + * @cb: a #PinosNodeInfoCallback * @cancelable: a #GCancellable * @callback: a #GAsyncReadyCallback to call when the operation is finished * @user_data: user data passed to @cb * - * Call @cb for each source. + * Call @cb for each node. */ void -pinos_context_list_source_info (PinosContext *context, - PinosSourceInfoFlags flags, - PinosSourceInfoCallback cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +pinos_context_list_node_info (PinosContext *context, + PinosNodeInfoFlags flags, + PinosNodeInfoCallback cb, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { PinosContextPrivate *priv; GList *walk; @@ -349,13 +346,13 @@ pinos_context_list_source_info (PinosContext *context, priv = context->priv; - for (walk = priv->sources; walk; walk = g_list_next (walk)) { + for (walk = priv->nodes; walk; walk = g_list_next (walk)) { GDBusProxy *proxy = walk->data; - PinosSourceInfo info; + PinosNodeInfo info; - source_fill_info (&info, proxy); + node_fill_info (&info, proxy); cb (context, &info, user_data); - source_clear_info (&info); + node_clear_info (&info); } g_task_return_boolean (task, TRUE); @@ -363,27 +360,27 @@ pinos_context_list_source_info (PinosContext *context, } /** - * pinos_context_get_source_info_by_id: + * pinos_context_get_node_info_by_id: * @context: a connected #PinosContext - * @id: a source id - * @flags: extra #PinosSourceInfoFlags - * @cb: a #PinosSourceInfoCallback + * @id: a node id + * @flags: extra #PinosNodeInfoFlags + * @cb: a #PinosNodeInfoCallback * @cancelable: a #GCancellable * @callback: a #GAsyncReadyCallback to call when the operation is finished * @user_data: user data passed to @cb * - * Call @cb for the source with @id. + * Call @cb for the node with @id. */ void -pinos_context_get_source_info_by_id (PinosContext *context, - gpointer id, - PinosSourceInfoFlags flags, - PinosSourceInfoCallback cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +pinos_context_get_node_info_by_id (PinosContext *context, + gpointer id, + PinosNodeInfoFlags flags, + PinosNodeInfoCallback cb, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - PinosSourceInfo info; + PinosNodeInfo info; GDBusProxy *proxy; GTask *task; @@ -395,53 +392,54 @@ pinos_context_get_source_info_by_id (PinosContext *context, proxy = G_DBUS_PROXY (id); - source_fill_info (&info, proxy); + node_fill_info (&info, proxy); cb (context, &info, user_data); - source_clear_info (&info); + node_clear_info (&info); g_task_return_boolean (task, TRUE); g_object_unref (task); } /** - * pinos_sink_state_as_string: - * @state: a #PinosSinkState + * pinos_direction_as_string: + * @direction: a #PinosDirection * - * Return the string representation of @state. + * Return the string representation of @direction. * - * Returns: the string representation of @state. + * Returns: the string representation of @direction. */ const gchar * -pinos_sink_state_as_string (PinosSinkState state) +pinos_direction_as_string (PinosDirection direction) { GEnumValue *val; - val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_SINK_STATE)), - state); + val = g_enum_get_value (G_ENUM_CLASS (g_type_class_ref (PINOS_TYPE_DIRECTION)), + direction); - return val == NULL ? "invalid-state" : val->value_nick; + return val == NULL ? "invalid-direction" : val->value_nick; } static void -sink_fill_info (PinosSinkInfo *info, GDBusProxy *proxy) +port_fill_info (PinosPortInfo *info, GDBusProxy *proxy) { GHashTable *changed = g_object_get_data (G_OBJECT (proxy), "pinos-changed-properties"); info->id = proxy; - info->sink_path = g_dbus_proxy_get_object_path (proxy); + info->port_path = g_dbus_proxy_get_object_path (proxy); + SET_UINT32 ("Direction", direction, 0, PINOS_DIRECTION_INVALID); + SET_STRING ("Node", node_path, 0); info->change_mask = 0; SET_STRING ("Name", name, 0); SET_PROPERTIES ("Properties", properties, 1); - SET_UINT32 ("State", state, 2, PINOS_SINK_STATE_ERROR); - SET_BYTES ("PossibleFormats", possible_formats, 3); + SET_BYTES ("PossibleFormats", possible_formats, 2); if (changed) g_hash_table_remove_all (changed); } static void -sink_clear_info (PinosSinkInfo *info) +port_clear_info (PinosPortInfo *info) { if (info->properties) pinos_properties_free (info->properties); @@ -450,20 +448,20 @@ sink_clear_info (PinosSinkInfo *info) } /** - * pinos_context_list_sink_info: + * pinos_context_list_port_info: * @context: a connected #PinosContext - * @flags: extra #PinosSinkInfoFlags - * @cb: a #PinosSinkInfoCallback + * @flags: extra #PinosPortInfoFlags + * @cb: a #PinosPortInfoCallback * @cancelable: a #GCancellable * @callback: a #GAsyncReadyCallback to call when the operation is finished * @user_data: user data passed to @cb * - * Call @cb for each sink. + * Call @cb for each port. */ void -pinos_context_list_sink_info (PinosContext *context, - PinosSinkInfoFlags flags, - PinosSinkInfoCallback cb, +pinos_context_list_port_info (PinosContext *context, + PinosPortInfoFlags flags, + PinosPortInfoCallback cb, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -479,13 +477,13 @@ pinos_context_list_sink_info (PinosContext *context, priv = context->priv; - for (walk = priv->sinks; walk; walk = g_list_next (walk)) { + for (walk = priv->ports; walk; walk = g_list_next (walk)) { GDBusProxy *proxy = walk->data; - PinosSinkInfo info; + PinosPortInfo info; - sink_fill_info (&info, proxy); + port_fill_info (&info, proxy); cb (context, &info, user_data); - sink_clear_info (&info); + port_clear_info (&info); } g_task_return_boolean (task, TRUE); @@ -493,27 +491,27 @@ pinos_context_list_sink_info (PinosContext *context, } /** - * pinos_context_get_sink_info_by_id: + * pinos_context_get_port_info_by_id: * @context: a connected #PinosContext - * @id: a sink id - * @flags: extra #PinosSinkInfoFlags - * @cb: a #PinosSinkInfoCallback + * @id: a port id + * @flags: extra #PinosPortInfoFlags + * @cb: a #PinosPortInfoCallback * @cancelable: a #GCancellable * @callback: a #GAsyncReadyCallback to call when the operation is finished * @user_data: user data passed to @cb * - * Call @cb for the sink with @id. + * Call @cb for the port with @id. */ void -pinos_context_get_sink_info_by_id (PinosContext *context, +pinos_context_get_port_info_by_id (PinosContext *context, gpointer id, - PinosSinkInfoFlags flags, - PinosSinkInfoCallback cb, + PinosPortInfoFlags flags, + PinosPortInfoCallback cb, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { - PinosSinkInfo info; + PinosPortInfo info; GDBusProxy *proxy; GTask *task; @@ -525,9 +523,9 @@ pinos_context_get_sink_info_by_id (PinosContext *context, proxy = G_DBUS_PROXY (id); - sink_fill_info (&info, proxy); + port_fill_info (&info, proxy); cb (context, &info, user_data); - sink_clear_info (&info); + port_clear_info (&info); g_task_return_boolean (task, TRUE); g_object_unref (task); @@ -559,14 +557,15 @@ channel_fill_info (PinosChannelInfo *info, GDBusProxy *proxy) info->id = proxy; info->channel_path = g_dbus_proxy_get_object_path (proxy); + SET_UINT32 ("Direction", direction, 2, PINOS_DIRECTION_INVALID); + SET_STRING ("Client", client_path, 0); info->change_mask = 0; - SET_STRING ("Client", client_path, 0); - SET_STRING ("Owner", owner_path, 1); - SET_BYTES ("PossibleFormats", possible_formats, 2); - SET_UINT32 ("State", state, 3, PINOS_CHANNEL_STATE_ERROR); + SET_STRING ("Port", port_path, 0); + SET_PROPERTIES ("Properties", properties, 1); + SET_UINT32 ("State", state, 2, PINOS_CHANNEL_STATE_ERROR); + SET_BYTES ("PossibleFormats", possible_formats, 3); SET_BYTES ("Format", format, 4); - SET_PROPERTIES ("Properties", properties, 5); if (changed) g_hash_table_remove_all (changed); @@ -577,6 +576,8 @@ channel_clear_info (PinosChannelInfo *info) { if (info->possible_formats) g_bytes_unref (info->possible_formats); + if (info->format) + g_bytes_unref (info->format); if (info->properties) pinos_properties_free (info->properties); } @@ -627,7 +628,7 @@ pinos_context_list_channel_info (PinosContext *context, /** * pinos_context_get_channel_info_by_id: * @context: a connected #PinosContext - * @id: a source output id + * @id: a channel id * @flags: extra #PinosChannelInfoFlags * @cb: a #PinosChannelInfoCallback * @cancelable: a #GCancellable diff --git a/pinos/client/introspect.h b/pinos/client/introspect.h index 9bb705e90..00eea3ebd 100644 --- a/pinos/client/introspect.h +++ b/pinos/client/introspect.h @@ -144,190 +144,167 @@ void pinos_context_get_client_info_by_id (PinosContext *context, gpointer user_data); /** - * PinosSourceState: - * @PINOS_SOURCE_STATE_ERROR: the source is in error - * @PINOS_SOURCE_STATE_SUSPENDED: the source is suspended, the device might + * PinosNodeState: + * @PINOS_NODE_STATE_ERROR: the node is in error + * @PINOS_NODE_STATE_SUSPENDED: the node is suspended, the device might * be closed - * @PINOS_SOURCE_STATE_INITIALIZING: the source is initializing, the device is + * @PINOS_NODE_STATE_INITIALIZING: the node is initializing, the device is * being opened and the capabilities are queried - * @PINOS_SOURCE_STATE_IDLE: the source is running but there is no active - * channel - * @PINOS_SOURCE_STATE_RUNNING: the source is running - * - * The different source states - */ -typedef enum { - PINOS_SOURCE_STATE_ERROR = -1, - PINOS_SOURCE_STATE_SUSPENDED = 0, - PINOS_SOURCE_STATE_INITIALIZING = 1, - PINOS_SOURCE_STATE_IDLE = 2, - PINOS_SOURCE_STATE_RUNNING = 3, -} PinosSourceState; - -const gchar * pinos_source_state_as_string (PinosSourceState state); - -/** - * PinosSourceInfo: - * @id: generic id of the source - * @source_path: the unique path of the source, suitable for connecting - * @change_mask: bitfield of changed fields since last call - * @name: name the source, suitable for display - * @properties: the properties of the source - * @state: the current state of the source - * @possible formats: the possible formats this source can produce - * - * The source information. Extra information can be added in later - * versions. - */ -typedef struct { - gpointer id; - const char *source_path; - guint64 change_mask; - const char *name; - PinosProperties *properties; - PinosSourceState state; - GBytes *possible_formats; -} PinosSourceInfo; - -/** - * PinosSourceInfoFlags: - * @PINOS_SOURCE_INFO_FLAGS_NONE: no flags - * @PINOS_SOURCE_INFO_FLAGS_FORMATS: include formats - * - * Extra flags to pass to pinos_context_get_source_info_list. - */ -typedef enum { - PINOS_SOURCE_INFO_FLAGS_NONE = 0, - PINOS_SOURCE_INFO_FLAGS_FORMATS = (1 << 0) -} PinosSourceInfoFlags; - -/** - * PinosSourceInfoCallback: - * @c: a #PinosContext - * @info: a #PinosSourceInfo - * @user_data: user data - * - * Callback with information about the Pinos source in @info. - */ -typedef void (*PinosSourceInfoCallback) (PinosContext *c, - const PinosSourceInfo *info, - gpointer user_data); - -void pinos_context_list_source_info (PinosContext *context, - PinosSourceInfoFlags flags, - PinosSourceInfoCallback cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -void pinos_context_get_source_info_by_id (PinosContext *context, - gpointer id, - PinosSourceInfoFlags flags, - PinosSourceInfoCallback cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -/** - * PinosSinkState: - * @PINOS_SINK_STATE_ERROR: the sink is in error - * @PINOS_SINK_STATE_SUSPENDED: the sink is suspended, the device might - * be closed - * @PINOS_SINK_STATE_INITIALIZING: the sink is initializing, the device is - * being opened and the capabilities are queried - * @PINOS_SINK_STATE_IDLE: the sink is running but there is no active + * @PINOS_NODE_STATE_IDLE: the node is running but there is no active * channel - * @PINOS_SINK_STATE_RUNNING: the sink is running + * @PINOS_NODE_STATE_RUNNING: the node is running * - * The different sink states + * The different node states */ typedef enum { - PINOS_SINK_STATE_ERROR = -1, - PINOS_SINK_STATE_SUSPENDED = 0, - PINOS_SINK_STATE_INITIALIZING = 1, - PINOS_SINK_STATE_IDLE = 2, - PINOS_SINK_STATE_RUNNING = 3, -} PinosSinkState; + PINOS_NODE_STATE_ERROR = -1, + PINOS_NODE_STATE_SUSPENDED = 0, + PINOS_NODE_STATE_INITIALIZING = 1, + PINOS_NODE_STATE_IDLE = 2, + PINOS_NODE_STATE_RUNNING = 3, +} PinosNodeState; -const gchar * pinos_sink_state_as_string (PinosSinkState state); +const gchar * pinos_node_state_as_string (PinosNodeState state); /** - * PinosSinkInfo: - * @id: generic id of the sink - * @sink_path: the unique path of the sink, suitable for connecting + * PinosNodeInfo: + * @id: generic id of the node + * @node_path: the unique path of the node * @change_mask: bitfield of changed fields since last call - * @name: name the sink, suitable for display - * @properties: the properties of the sink - * @state: the current state of the sink - * @possible formats: the possible formats this sink can consume + * @name: name the node, suitable for display + * @properties: the properties of the node + * @state: the current state of the node * - * The sink information. Extra information can be added in later + * The node information. Extra information can be added in later * versions. */ typedef struct { gpointer id; - const char *sink_path; + const char *node_path; guint64 change_mask; const char *name; PinosProperties *properties; - PinosSinkState state; - GBytes *possible_formats; -} PinosSinkInfo; + PinosNodeState state; +} PinosNodeInfo; /** - * PinosSinkInfoFlags: - * @PINOS_SINK_INFO_FLAGS_NONE: no flags - * @PINOS_SINK_INFO_FLAGS_FORMATS: include formats + * PinosNodeInfoFlags: + * @PINOS_NODE_INFO_FLAGS_NONE: no flags * - * Extra flags to pass to pinos_context_get_sink_info_list. + * Extra flags to pass to pinos_context_get_node_info_list. */ typedef enum { - PINOS_SINK_INFO_FLAGS_NONE = 0, - PINOS_SINK_INFO_FLAGS_FORMATS = (1 << 0) -} PinosSinkInfoFlags; + PINOS_NODE_INFO_FLAGS_NONE = 0, +} PinosNodeInfoFlags; /** - * PinosSinkInfoCallback: + * PinosNodeInfoCallback: * @c: a #PinosContext - * @info: a #PinosSinkInfo + * @info: a #PinosNodeInfo * @user_data: user data * - * Callback with information about the Pinos sink in @info. + * Callback with information about the Pinos node in @info. */ -typedef void (*PinosSinkInfoCallback) (PinosContext *c, - const PinosSinkInfo *info, +typedef void (*PinosNodeInfoCallback) (PinosContext *c, + const PinosNodeInfo *info, gpointer user_data); -void pinos_context_list_sink_info (PinosContext *context, - PinosSinkInfoFlags flags, - PinosSinkInfoCallback cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -void pinos_context_get_sink_info_by_id (PinosContext *context, - gpointer id, - PinosSinkInfoFlags flags, - PinosSinkInfoCallback cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +void pinos_context_list_node_info (PinosContext *context, + PinosNodeInfoFlags flags, + PinosNodeInfoCallback cb, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +void pinos_context_get_node_info_by_id (PinosContext *context, + gpointer id, + PinosNodeInfoFlags flags, + PinosNodeInfoCallback cb, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + /** - * PinosChannelType: - * @PINOS_CHANNEL_TYPE_UNKNOWN: an unknown channel type - * @PINOS_CHANNEL_TYPE_INPUT: an input channel type - * @PINOS_CHANNEL_TYPE_OUTPUT: an output channel type + * PinosDirection: + * @PINOS_DIRECTION_INVALID: invalid direction + * @PINOS_DIRECTION_INPUT: an input port/channel + * @PINOS_DIRECTION_OUTPUT: an output port/channel * - * The different channel states + * The direction of a port or channel */ typedef enum { - PINOS_CHANNEL_TYPE_UNKNOWN = 0, - PINOS_CHANNEL_TYPE_INPUT = 1, - PINOS_CHANNEL_TYPE_OUTPUT = 2, -} PinosChannelType; + PINOS_DIRECTION_INVALID = -1, + PINOS_DIRECTION_INPUT = 0, + PINOS_DIRECTION_OUTPUT = 1 +} PinosDirection; +const gchar * pinos_direction_as_string (PinosDirection direction); + +/** + * PinosPortInfo: + * @id: generic id of the port + * @port_path: the unique path of the port, suitable for connecting + * @node_path: the node path of the port + * @direction: the direction of the port + * @change_mask: bitfield of changed fields since last call + * @name: name the port, suitable for display + * @properties: the properties of the port + * @possible formats: the possible formats this port can consume + * + * The port information. Extra information can be added in later + * versions. + */ +typedef struct { + gpointer id; + const char *port_path; + const char *node_path; + PinosDirection direction; + guint64 change_mask; + const char *name; + PinosProperties *properties; + GBytes *possible_formats; +} PinosPortInfo; + +/** + * PinosPortInfoFlags: + * @PINOS_PORT_INFO_FLAGS_NONE: no flags + * @PINOS_PORT_INFO_FLAGS_FORMATS: include formats + * + * Extra flags to pass to pinos_context_get_port_info_list. + */ +typedef enum { + PINOS_PORT_INFO_FLAGS_NONE = 0, + PINOS_PORT_INFO_FLAGS_FORMATS = (1 << 0) +} PinosPortInfoFlags; + +/** + * PinosPortInfoCallback: + * @c: a #PinosContext + * @info: a #PinosPortInfo + * @user_data: user data + * + * Callback with information about the Pinos port in @info. + */ +typedef void (*PinosPortInfoCallback) (PinosContext *c, + const PinosPortInfo *info, + gpointer user_data); + +void pinos_context_list_port_info (PinosContext *context, + PinosPortInfoFlags flags, + PinosPortInfoCallback cb, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +void pinos_context_get_port_info_by_id (PinosContext *context, + gpointer id, + PinosPortInfoFlags flags, + PinosPortInfoCallback cb, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); /** * PinosChannelState: * @PINOS_CHANNEL_STATE_ERROR: the channel is in error - * @PINOS_CHANNEL_STATE_IDLE: the channel is idle + * @PINOS_CHANNEL_STATE_STOPPED: the channel is stopped * @PINOS_CHANNEL_STATE_STARTING: the channel is starting * @PINOS_CHANNEL_STATE_STREAMING: the channel is streaming * @@ -335,7 +312,7 @@ typedef enum { */ typedef enum { PINOS_CHANNEL_STATE_ERROR = -1, - PINOS_CHANNEL_STATE_IDLE = 0, + PINOS_CHANNEL_STATE_STOPPED = 0, PINOS_CHANNEL_STATE_STARTING = 1, PINOS_CHANNEL_STATE_STREAMING = 2, } PinosChannelState; @@ -346,14 +323,14 @@ const gchar * pinos_channel_state_as_string (PinosChannelState state); * PinosChannelInfo: * @id: generic id of the channel_ * @channel_path: the unique path of the channel - * @change_mask: bitfield of changed fields since last call + * @direction: the channel direction * @client_path: the owner client - * @owner_path: the owner source or sink path - * @type: the channel type - * @possible_formats: the possible formats - * @state: the state - * @format: when streaming, the current format + * @change_mask: bitfield of changed fields since last call + * @port_path: the owner port * @properties: the properties of the channel + * @state: the state + * @possible_formats: the possible formats + * @format: when streaming, the current format * * The channel information. Extra information can be added in later * versions. @@ -361,29 +338,29 @@ const gchar * pinos_channel_state_as_string (PinosChannelState state); typedef struct { gpointer id; const char *channel_path; - guint64 change_mask; + PinosDirection direction; const char *client_path; - const char *owner_path; - PinosChannelType type; - GBytes *possible_formats; - PinosChannelState state; - GBytes *format; + guint64 change_mask; + const char *port_path; PinosProperties *properties; + PinosChannelState state; + GBytes *possible_formats; + GBytes *format; } PinosChannelInfo; /** * PinosChannelInfoFlags: * @PINOS_CHANNEL_INFO_FLAGS_NONE: no flags - * @PINOS_CHANNEL_INFO_FLAGS_NO_SOURCE: don't list source channels - * @PINOS_CHANNEL_INFO_FLAGS_NO_SINK: don't list sink channels + * @PINOS_CHANNEL_INFO_FLAGS_NO_INPUT: don't list input channels + * @PINOS_CHANNEL_INFO_FLAGS_NO_OUTPUT: don't list output channels * * Extra flags to pass to pinos_context_list_channel_info() and * pinos_context_get_channel_info_by_id(). */ typedef enum { PINOS_CHANNEL_INFO_FLAGS_NONE = 0, - PINOS_CHANNEL_INFO_FLAGS_NO_SOURCE = (1 << 0), - PINOS_CHANNEL_INFO_FLAGS_NO_SINK = (1 << 1), + PINOS_CHANNEL_INFO_FLAGS_NO_INPUT = (1 << 0), + PINOS_CHANNEL_INFO_FLAGS_NO_OUTPUT = (1 << 1), } PinosChannelInfoFlags; /** diff --git a/pinos/client/pinos.h b/pinos/client/pinos.h index 3129c1e8f..5e4eb4dd5 100644 --- a/pinos/client/pinos.h +++ b/pinos/client/pinos.h @@ -34,6 +34,7 @@ #define PINOS_DBUS_OBJECT_PREFIX "/org/pinos" #define PINOS_DBUS_OBJECT_SERVER PINOS_DBUS_OBJECT_PREFIX "/server" #define PINOS_DBUS_OBJECT_NODE PINOS_DBUS_OBJECT_PREFIX "/node" +#define PINOS_DBUS_OBJECT_PORT PINOS_DBUS_OBJECT_PREFIX "/port" #define PINOS_DBUS_OBJECT_CLIENT PINOS_DBUS_OBJECT_PREFIX "/client" void pinos_init (int *argc, char **argv[]); diff --git a/pinos/client/private.h b/pinos/client/private.h index e7b147e03..f0956c4f7 100644 --- a/pinos/client/private.h +++ b/pinos/client/private.h @@ -40,8 +40,8 @@ struct _PinosContextPrivate PinosSubscribe *subscribe; GList *clients; - GList *sources; - GList *sinks; + GList *nodes; + GList *ports; GList *channels; }; diff --git a/pinos/client/stream.c b/pinos/client/stream.c index 5008d5138..4feb5f851 100644 --- a/pinos/client/stream.c +++ b/pinos/client/stream.c @@ -39,6 +39,7 @@ struct _PinosStreamPrivate PinosStreamState state; GError *error; + PinosDirection direction; gchar *path; GBytes *possible_formats; gboolean provide; @@ -596,14 +597,15 @@ create_failed: } static gboolean -do_connect_source (PinosStream *stream) +do_connect (PinosStream *stream) { PinosStreamPrivate *priv = stream->priv; PinosContext *context = priv->context; g_dbus_proxy_call (context->priv->client, - "CreateSourceChannel", - g_variant_new ("(ss@a{sv})", + "CreateChannel", + g_variant_new ("(uss@a{sv})", + priv->direction, (priv->path ? priv->path : ""), g_bytes_get_data (priv->possible_formats, NULL), pinos_properties_to_variant (priv->properties)), @@ -617,21 +619,23 @@ do_connect_source (PinosStream *stream) } /** - * pinos_stream_connect_source: + * pinos_stream_connect: * @stream: a #PinosStream - * @source_path: the source path to connect to + * @direction: the stream direction + * @port_path: the port path to connect to or %NULL to get the default port * @flags: a #PinosStreamFlags * @possible_formats: (transfer full): a #GBytes with possible accepted formats * - * Connect @stream for capturing from @source_path. + * Connect @stream for input or output on @port_path. * * Returns: %TRUE on success. */ gboolean -pinos_stream_connect_source (PinosStream *stream, - const gchar *source_path, - PinosStreamFlags flags, - GBytes *possible_formats) +pinos_stream_connect (PinosStream *stream, + PinosDirection direction, + const gchar *port_path, + PinosStreamFlags flags, + GBytes *possible_formats) { PinosStreamPrivate *priv; PinosContext *context; @@ -644,8 +648,9 @@ pinos_stream_connect_source (PinosStream *stream, g_return_val_if_fail (pinos_context_get_state (context) == PINOS_CONTEXT_STATE_READY, FALSE); g_return_val_if_fail (pinos_stream_get_state (stream) == PINOS_STREAM_STATE_UNCONNECTED, FALSE); + priv->direction = direction; g_free (priv->path); - priv->path = g_strdup (source_path); + priv->path = g_strdup (port_path); if (priv->possible_formats) g_bytes_unref (priv->possible_formats); priv->possible_formats = possible_formats; @@ -654,72 +659,7 @@ pinos_stream_connect_source (PinosStream *stream, stream_set_state (stream, PINOS_STREAM_STATE_CONNECTING, NULL); g_main_context_invoke (context->priv->context, - (GSourceFunc) do_connect_source, - g_object_ref (stream)); - - return TRUE; -} - -static gboolean -do_connect_sink (PinosStream *stream) -{ - PinosStreamPrivate *priv = stream->priv; - PinosContext *context = priv->context; - - g_dbus_proxy_call (context->priv->client, - "CreateSinkChannel", - g_variant_new ("(ss@a{sv})", - (priv->path ? priv->path : ""), - g_bytes_get_data (priv->possible_formats, NULL), - pinos_properties_to_variant (priv->properties)), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* GCancellable *cancellable */ - on_channel_created, - stream); - - return FALSE; -} - -/** - * pinos_stream_connect_sink: - * @stream: a #PinosStream - * @sink_path: the sink path to connect to - * @flags: a #PinosStreamFlags - * @possible_formats: (transfer full): a #GBytes with possible accepted formats - * - * Connect @stream for playback to @sink_path. - * - * Returns: %TRUE on success. - */ -gboolean -pinos_stream_connect_sink (PinosStream *stream, - const gchar *sink_path, - PinosStreamFlags flags, - GBytes *possible_formats) -{ - PinosStreamPrivate *priv; - PinosContext *context; - - g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE); - g_return_val_if_fail (possible_formats != NULL, FALSE); - - priv = stream->priv; - context = priv->context; - g_return_val_if_fail (pinos_context_get_state (context) == PINOS_CONTEXT_STATE_READY, FALSE); - g_return_val_if_fail (pinos_stream_get_state (stream) == PINOS_STREAM_STATE_UNCONNECTED, FALSE); - - g_free (priv->path); - priv->path = g_strdup (sink_path); - if (priv->possible_formats) - g_bytes_unref (priv->possible_formats); - priv->possible_formats = possible_formats; - priv->provide = FALSE; - - stream_set_state (stream, PINOS_STREAM_STATE_CONNECTING, NULL); - - g_main_context_invoke (context->priv->context, - (GSourceFunc) do_connect_sink, + (GSourceFunc) do_connect, g_object_ref (stream)); return TRUE; diff --git a/pinos/client/stream.h b/pinos/client/stream.h index 6fc68feb8..5e6edc35d 100644 --- a/pinos/client/stream.h +++ b/pinos/client/stream.h @@ -91,12 +91,9 @@ PinosStream * pinos_stream_new (PinosContext *context, PinosStreamState pinos_stream_get_state (PinosStream *stream); const GError * pinos_stream_get_error (PinosStream *stream); -gboolean pinos_stream_connect_source (PinosStream *stream, - const gchar *source_path, - PinosStreamFlags flags, - GBytes *possible_formats); -gboolean pinos_stream_connect_sink (PinosStream *stream, - const gchar *sink_path, +gboolean pinos_stream_connect (PinosStream *stream, + PinosDirection direction, + const gchar *port_path, PinosStreamFlags flags, GBytes *possible_formats); gboolean pinos_stream_connect_provide (PinosStream *stream, diff --git a/pinos/client/subscribe.c b/pinos/client/subscribe.c index 6b18911b6..50a6b6193 100644 --- a/pinos/client/subscribe.c +++ b/pinos/client/subscribe.c @@ -105,11 +105,11 @@ notify_event (PinosSubscribe *subscribe, else if (g_strcmp0 (interface_name, "org.pinos.Client1") == 0) { flags = PINOS_SUBSCRIPTION_FLAG_CLIENT; } - else if (g_strcmp0 (interface_name, "org.pinos.Source1") == 0) { - flags = PINOS_SUBSCRIPTION_FLAG_SOURCE; + else if (g_strcmp0 (interface_name, "org.pinos.Node1") == 0) { + flags = PINOS_SUBSCRIPTION_FLAG_NODE; } - else if (g_strcmp0 (interface_name, "org.pinos.Sink1") == 0) { - flags = PINOS_SUBSCRIPTION_FLAG_SINK; + else if (g_strcmp0 (interface_name, "org.pinos.Port1") == 0) { + flags = PINOS_SUBSCRIPTION_FLAG_PORT; } else if (g_strcmp0 (interface_name, "org.pinos.Channel1") == 0) { flags = PINOS_SUBSCRIPTION_FLAG_CHANNEL; diff --git a/pinos/client/subscribe.h b/pinos/client/subscribe.h index 23b0d4614..b022000d5 100644 --- a/pinos/client/subscribe.h +++ b/pinos/client/subscribe.h @@ -47,8 +47,8 @@ typedef enum { typedef enum { PINOS_SUBSCRIPTION_FLAG_DAEMON = (1 << 0), PINOS_SUBSCRIPTION_FLAG_CLIENT = (1 << 1), - PINOS_SUBSCRIPTION_FLAG_SOURCE = (1 << 2), - PINOS_SUBSCRIPTION_FLAG_SINK = (1 << 3), + PINOS_SUBSCRIPTION_FLAG_NODE = (1 << 2), + PINOS_SUBSCRIPTION_FLAG_PORT = (1 << 3), PINOS_SUBSCRIPTION_FLAG_CHANNEL = (1 << 4), } PinosSubscriptionFlags; diff --git a/pinos/dbus/org.pinos.xml b/pinos/dbus/org.pinos.xml index 0ec8952a5..7444d2d85 100644 --- a/pinos/dbus/org.pinos.xml +++ b/pinos/dbus/org.pinos.xml @@ -54,34 +54,22 @@ --> - - + + + - - - - - - - - - + + - + - + + + + + + + + + + + + - - - - - - - - - - - - - - - + + - + + + + + + + - - - - - diff --git a/pinos/gst/gstpinosdeviceprovider.c b/pinos/gst/gstpinosdeviceprovider.c index fd1a13441..cfaf95d27 100644 --- a/pinos/gst/gstpinosdeviceprovider.c +++ b/pinos/gst/gstpinosdeviceprovider.c @@ -191,19 +191,21 @@ enum }; static GstDevice * -new_source (const PinosSourceInfo *info) +new_node (const PinosNodeInfo *info) { GstCaps *caps; GstStructure *props; gpointer state = NULL; const gchar *klass; + /* FIXME, iterate ports */ +#if 0 if (info->possible_formats) caps = gst_caps_from_string (g_bytes_get_data (info->possible_formats, NULL)); else +#endif caps = gst_caps_new_any(); - props = gst_structure_new_empty ("pinos-proplist"); while (TRUE) { @@ -223,21 +225,21 @@ new_source (const PinosSourceInfo *info) return gst_pinos_device_new (info->id, info->name, caps, - info->source_path, + info->node_path, klass, GST_PINOS_DEVICE_TYPE_SOURCE, props); } static void -get_source_info_cb (PinosContext *context, - const PinosSourceInfo *info, - gpointer user_data) +get_node_info_cb (PinosContext *context, + const PinosNodeInfo *info, + gpointer user_data) { GstPinosDeviceProvider *self = user_data; GstDevice *dev; - dev = new_source (info); + dev = new_node (info); if (dev) gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), dev); } @@ -273,22 +275,22 @@ context_subscribe_cb (PinosContext *context, GstDeviceProvider *provider = user_data; GstPinosDevice *dev; - if (flags != PINOS_SUBSCRIPTION_FLAG_SOURCE) + if (flags != PINOS_SUBSCRIPTION_FLAG_NODE) return; dev = find_device (provider, id); if (type == PINOS_SUBSCRIPTION_EVENT_NEW) { - if (flags == PINOS_SUBSCRIPTION_FLAG_SOURCE && dev == NULL) - pinos_context_get_source_info_by_id (context, - id, - PINOS_SOURCE_INFO_FLAGS_FORMATS, - get_source_info_cb, - NULL, - NULL, - self); + if (flags == PINOS_SUBSCRIPTION_FLAG_NODE && dev == NULL) + pinos_context_get_node_info_by_id (context, + id, + PINOS_NODE_INFO_FLAGS_NONE, + get_node_info_cb, + NULL, + NULL, + self); } else if (type == PINOS_SUBSCRIPTION_EVENT_REMOVE) { - if (flags == PINOS_SUBSCRIPTION_FLAG_SOURCE && dev != NULL) { + if (flags == PINOS_SUBSCRIPTION_FLAG_NODE && dev != NULL) { gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self), GST_DEVICE (dev)); } @@ -303,25 +305,25 @@ typedef struct { } InfoData; static void -list_source_info_cb (PinosContext *c, - const PinosSourceInfo *info, - gpointer user_data) +list_node_info_cb (PinosContext *c, + const PinosNodeInfo *info, + gpointer user_data) { InfoData *data = user_data; - *data->devices = g_list_prepend (*data->devices, gst_object_ref_sink (new_source (info))); + *data->devices = g_list_prepend (*data->devices, gst_object_ref_sink (new_node (info))); } static void -list_source_info_end_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +list_node_info_end_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { InfoData *data = user_data; GError *error = NULL; if (!pinos_context_info_finish (source_object, res, &error)) { - GST_WARNING_OBJECT (source_object, "failed to list sources: %s", error->message); + GST_WARNING_OBJECT (source_object, "failed to list nodes: %s", error->message); g_clear_error (&error); } data->end = TRUE; @@ -396,12 +398,12 @@ gst_pinos_device_provider_probe (GstDeviceProvider * provider) data.end = FALSE; data.devices = NULL; - pinos_context_list_source_info (c, - PINOS_SOURCE_INFO_FLAGS_FORMATS, - list_source_info_cb, - NULL, - list_source_info_end_cb, - &data); + pinos_context_list_node_info (c, + PINOS_NODE_INFO_FLAGS_NONE, + list_node_info_cb, + NULL, + list_node_info_end_cb, + &data); for (;;) { if (pinos_context_get_state (c) <= 0) break; diff --git a/pinos/gst/gstpinossink.c b/pinos/gst/gstpinossink.c index c3d163c8f..d72cc46b7 100644 --- a/pinos/gst/gstpinossink.c +++ b/pinos/gst/gstpinossink.c @@ -366,12 +366,14 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) { GstPinosSink *pinossink; gchar *str; + GBytes *format; PinosStreamState state; gboolean res = FALSE; pinossink = GST_PINOS_SINK (bsink); str = gst_caps_to_string (caps); + format = g_bytes_new_take (str, strlen (str) + 1); pinos_main_loop_lock (pinossink->loop); state = pinos_stream_get_state (pinossink->stream); @@ -379,6 +381,26 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) if (state == PINOS_STREAM_STATE_ERROR) goto start_error; + if (state == PINOS_STREAM_STATE_UNCONNECTED) { + pinos_stream_connect (pinossink->stream, + PINOS_DIRECTION_INPUT, + pinossink->path, + 0, + g_bytes_ref (format)); + + while (TRUE) { + state = pinos_stream_get_state (pinossink->stream); + + if (state == PINOS_STREAM_STATE_READY) + break; + + if (state == PINOS_STREAM_STATE_ERROR) + goto start_error; + + pinos_main_loop_wait (pinossink->loop); + } + } + if (state == PINOS_STREAM_STATE_STREAMING) { PinosBufferBuilder builder; PinosPacketFormatChange change; @@ -387,16 +409,16 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) pinos_buffer_builder_init (&builder); change.id = 1; - change.format = str; + change.format = g_bytes_get_data (format, NULL); pinos_buffer_builder_add_format_change (&builder, &change); pinos_buffer_builder_end (&builder, &pbuf); res = pinos_stream_send_buffer (pinossink->stream, &pbuf); pinos_buffer_clear (&pbuf); } else { - GBytes *format = g_bytes_new_take (str, strlen (str) + 1); - - res = pinos_stream_start (pinossink->stream, format, PINOS_STREAM_MODE_BUFFER); + res = pinos_stream_start (pinossink->stream, + g_bytes_ref (format), + PINOS_STREAM_MODE_BUFFER); while (TRUE) { state = pinos_stream_get_state (pinossink->stream); @@ -411,6 +433,7 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) } } pinos_main_loop_unlock (pinossink->loop); + g_bytes_unref (format); pinossink->negotiated = res; @@ -420,6 +443,7 @@ start_error: { GST_ERROR ("could not start stream"); pinos_main_loop_unlock (pinossink->loop); + g_bytes_unref (format); return FALSE; } } @@ -563,32 +587,9 @@ gst_pinos_sink_start (GstBaseSink * basesink) pinossink->stream = pinos_stream_new (pinossink->ctx, pinossink->client_name, props); g_signal_connect (pinossink->stream, "notify::state", (GCallback) on_stream_notify, pinossink); g_signal_connect (pinossink->stream, "new-buffer", (GCallback) on_new_buffer, pinossink); - - pinos_stream_connect_sink (pinossink->stream, pinossink->path, 0, g_bytes_new_static ("ANY", strlen ("ANY")+1)); - - while (TRUE) { - PinosStreamState state = pinos_stream_get_state (pinossink->stream); - - if (state == PINOS_STREAM_STATE_READY) - break; - - if (state == PINOS_STREAM_STATE_ERROR) - goto connect_error; - - pinos_main_loop_wait (pinossink->loop); - } pinos_main_loop_unlock (pinossink->loop); - pinossink->negotiated = TRUE; - return TRUE; - -connect_error: - { - GST_ERROR ("could not connect stream"); - pinos_main_loop_unlock (pinossink->loop); - return FALSE; - } } static gboolean diff --git a/pinos/gst/gstpinossrc.c b/pinos/gst/gstpinossrc.c index 02b36f8e0..7d3d38d82 100644 --- a/pinos/gst/gstpinossrc.c +++ b/pinos/gst/gstpinossrc.c @@ -648,7 +648,7 @@ gst_pinos_src_negotiate (GstBaseSrc * basesrc) } GST_DEBUG_OBJECT (basesrc, "connect capture with path %s", pinossrc->path); - pinos_stream_connect_source (pinossrc->stream, pinossrc->path, 0, accepted); + pinos_stream_connect (pinossrc->stream, PINOS_DIRECTION_OUTPUT, pinossrc->path, 0, accepted); while (TRUE) { PinosStreamState state = pinos_stream_get_state (pinossrc->stream); diff --git a/pinos/modules/gst/gst-manager.c b/pinos/modules/gst/gst-manager.c index ffa1869af..f7a48d90e 100644 --- a/pinos/modules/gst/gst-manager.c +++ b/pinos/modules/gst/gst-manager.c @@ -65,7 +65,7 @@ device_added (PinosGstManager *manager, PinosGstManagerPrivate *priv = manager->priv; gchar *name, *klass; GstElement *element; - PinosNode *node; + PinosNode *node = NULL; GstStructure *p; PinosProperties *properties; GstCaps *caps; @@ -91,24 +91,24 @@ device_added (PinosGstManager *manager, "gstreamer.device.class", klass); - node = pinos_node_new (priv->daemon); - g_object_set_data (G_OBJECT (device), "PinosNode", node); element = gst_device_create_element (device, NULL); if (strstr (klass, "Source")) { - pinos_gst_source_new (node, - name, - properties, - element, - caps); + node = pinos_gst_source_new (priv->daemon, + name, + properties, + element, + caps); } else if (strstr (klass, "Sink")) { - pinos_gst_sink_new (node, - name, - properties, - element, - caps); + node = pinos_gst_sink_new (priv->daemon, + name, + properties, + element, + caps); } + if (node) + g_object_set_data (G_OBJECT (device), "PinosNode", node); pinos_properties_free (properties); gst_caps_unref (caps); diff --git a/pinos/modules/gst/gst-sink.c b/pinos/modules/gst/gst-sink.c index b5e58ced3..8063507e6 100644 --- a/pinos/modules/gst/gst-sink.c +++ b/pinos/modules/gst/gst-sink.c @@ -35,6 +35,7 @@ struct _PinosGstSinkPrivate GstElement *depay; GstElement *element; + PinosPort *input; GstCaps *possible_formats; GstNetTimeProvider *provider; @@ -50,15 +51,15 @@ enum { PROP_POSSIBLE_FORMATS }; -G_DEFINE_TYPE (PinosGstSink, pinos_gst_sink, PINOS_TYPE_SINK); +G_DEFINE_TYPE (PinosGstSink, pinos_gst_sink, PINOS_TYPE_NODE); static gboolean bus_handler (GstBus *bus, GstMessage *message, gpointer user_data) { - PinosSink *sink = user_data; - PinosGstSinkPrivate *priv = PINOS_GST_SINK (sink)->priv; + PinosNode *node = user_data; + PinosGstSinkPrivate *priv = PINOS_GST_SINK (node)->priv; switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_ERROR: @@ -70,7 +71,7 @@ bus_handler (GstBus *bus, g_warning ("got error %s (%s)\n", error->message, debug); g_free (debug); - pinos_sink_report_error (sink, error); + pinos_node_report_error (node, error); gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; } @@ -82,9 +83,9 @@ bus_handler (GstBus *bus, gst_message_parse_new_clock (message, &clock); GST_INFO ("got new clock %s", GST_OBJECT_NAME (clock)); - g_object_get (sink, "properties", &props, NULL); + g_object_get (node, "properties", &props, NULL); pinos_properties_set (props, "gst.pipeline.clock", GST_OBJECT_NAME (clock)); - g_object_set (sink, "properties", props, NULL); + g_object_set (node, "properties", props, NULL); pinos_properties_free (props); break; } @@ -96,9 +97,9 @@ bus_handler (GstBus *bus, gst_message_parse_new_clock (message, &clock); GST_INFO ("clock lost %s", GST_OBJECT_NAME (clock)); - g_object_get (sink, "properties", &props, NULL); + g_object_get (node, "properties", &props, NULL); pinos_properties_remove (props, "gst.pipeline.clock"); - g_object_set (sink, "properties", props, NULL); + g_object_set (node, "properties", props, NULL); pinos_properties_free (props); gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); @@ -146,10 +147,12 @@ static gboolean start_pipeline (PinosGstSink *sink, GError **error) { PinosGstSinkPrivate *priv = sink->priv; +#if 0 GstCaps *caps; GstQuery *query; - GstStateChangeReturn ret; gchar *str; +#endif + GstStateChangeReturn ret; g_debug ("gst-sink %p: starting pipeline", sink); @@ -157,6 +160,7 @@ start_pipeline (PinosGstSink *sink, GError **error) if (ret == GST_STATE_CHANGE_FAILURE) goto ready_failed; +#if 0 query = gst_query_new_caps (NULL); if (gst_element_query (priv->element, query)) { gst_query_parse_caps_result (query, &caps); @@ -166,6 +170,7 @@ start_pipeline (PinosGstSink *sink, GError **error) g_free (str); } gst_query_unref (query); +#endif return TRUE; @@ -204,96 +209,37 @@ destroy_pipeline (PinosGstSink *sink) static gboolean -set_state (PinosSink *sink, - PinosSinkState state) +set_state (PinosNode *node, + PinosNodeState state) { - PinosGstSinkPrivate *priv = PINOS_GST_SINK (sink)->priv; + PinosGstSinkPrivate *priv = PINOS_GST_SINK (node)->priv; - g_debug ("gst-sink %p: set state %d", sink, state); + g_debug ("gst-sink %p: set state %s", node, pinos_node_state_as_string (state)); switch (state) { - case PINOS_SINK_STATE_SUSPENDED: + case PINOS_NODE_STATE_SUSPENDED: gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; - case PINOS_SINK_STATE_INITIALIZING: + case PINOS_NODE_STATE_INITIALIZING: gst_element_set_state (priv->pipeline, GST_STATE_READY); break; - case PINOS_SINK_STATE_IDLE: + case PINOS_NODE_STATE_IDLE: gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); break; - case PINOS_SINK_STATE_RUNNING: + case PINOS_NODE_STATE_RUNNING: gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); break; - case PINOS_SINK_STATE_ERROR: + case PINOS_NODE_STATE_ERROR: break; } - pinos_sink_update_state (sink, state); + pinos_node_update_state (node, state); return TRUE; } -static GBytes * -get_formats (PinosSink *sink, - GBytes *filter, - GError **error) -{ - PinosGstSinkPrivate *priv = PINOS_GST_SINK (sink)->priv; - GstCaps *caps, *cfilter; - gchar *str; - - if (filter) { - cfilter = gst_caps_from_string (g_bytes_get_data (filter, NULL)); - if (cfilter == NULL) - goto invalid_filter; - - caps = gst_caps_intersect (priv->possible_formats, cfilter); - gst_caps_unref (cfilter); - - if (caps == NULL) - goto no_formats; - - } else { - caps = gst_caps_ref (priv->possible_formats); - } - g_object_get (priv->depay, "caps", &cfilter, NULL); - if (cfilter != NULL) { - GstCaps *t = caps; - - caps = gst_caps_intersect (t, cfilter); - gst_caps_unref (cfilter); - gst_caps_unref (t); - } - if (gst_caps_is_empty (caps)) { - gst_caps_unref (caps); - goto no_formats; - } - - str = gst_caps_to_string (caps); - gst_caps_unref (caps); - - return g_bytes_new_take (str, strlen (str) + 1); - -invalid_filter: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Invalid filter received"); - return NULL; - } -no_formats: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "No compatible format found"); - return NULL; - } -} - static void on_socket_notify (GObject *gobject, GParamSpec *pspec, @@ -322,7 +268,7 @@ on_socket_notify (GObject *gobject, } if (num_handles == 0) { - pinos_sink_report_idle (PINOS_SINK (sink)); + pinos_node_report_idle (PINOS_NODE (sink)); g_object_set (priv->depay, "caps", NULL, NULL); str = gst_caps_to_string (priv->possible_formats); @@ -352,13 +298,9 @@ on_socket_notify (GObject *gobject, } /* this is what we use as the final format for the output */ g_object_set (gobject, "format", format, NULL); - pinos_sink_report_busy (PINOS_SINK (sink)); + pinos_node_report_busy (PINOS_NODE (sink)); g_object_unref (socket); } - if (format) { - pinos_sink_update_possible_formats (PINOS_SINK (sink), format); - g_bytes_unref (format); - } g_object_get (gobject, "properties", &props, NULL); while ((key = pinos_properties_iterate (priv->props, &state))) { @@ -369,64 +311,29 @@ on_socket_notify (GObject *gobject, pinos_properties_free (props); } -static PinosChannel * -create_channel (PinosSink *sink, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error) +static void +on_channel_added (PinosPort *port, PinosChannel *channel, PinosGstSink *sink) { - PinosGstSink *s = PINOS_GST_SINK (sink); - PinosGstSinkPrivate *priv = s->priv; - PinosChannel *channel; - gpointer state = NULL; - const gchar *key, *val; + PinosGstSinkPrivate *priv = sink->priv; + GError *error = NULL; if (priv->n_channels == 0) { - if (!start_pipeline (s, error)) - return NULL; + if (!start_pipeline (sink, &error)) + return; } - while ((key = pinos_properties_iterate (priv->props, &state))) { - val = pinos_properties_get (priv->props, key); - pinos_properties_set (props, key, val); - } - - channel = PINOS_SINK_CLASS (pinos_gst_sink_parent_class) - ->create_channel (sink, - client_path, - format_filter, - props, - prefix, - error); - if (channel == NULL) - goto no_channel; - - g_signal_connect (channel, - "notify::socket", - (GCallback) on_socket_notify, - sink); - + g_signal_connect (channel, "notify::socket", (GCallback) on_socket_notify, sink); priv->n_channels++; - - return channel; - - /* ERRORS */ -no_channel: - { - if (priv->n_channels == 0) - stop_pipeline (s); - return NULL; - } } -static gboolean -release_channel (PinosSink *sink, - PinosChannel *channel) +static void +on_channel_removed (PinosPort *port, PinosChannel *channel, PinosGstSink *sink) { - return PINOS_SINK_CLASS (pinos_gst_sink_parent_class) - ->release_channel (sink, channel); + PinosGstSinkPrivate *priv = sink->priv; + + priv->n_channels--; + if (priv->n_channels == 0) + stop_pipeline (sink); } static void @@ -481,10 +388,26 @@ static void sink_constructed (GObject * object) { PinosGstSink *sink = PINOS_GST_SINK (object); - - setup_pipeline (sink, NULL); + PinosGstSinkPrivate *priv = sink->priv; + gchar *str; + GBytes *format; G_OBJECT_CLASS (pinos_gst_sink_parent_class)->constructed (object); + + str = gst_caps_to_string (priv->possible_formats); + format = g_bytes_new_take (str, strlen (str) + 1); + + priv->input = pinos_port_new (PINOS_NODE (sink), + PINOS_DIRECTION_INPUT, + "input", + format, + NULL); + g_bytes_unref (format); + + g_signal_connect (priv->input, "channel-added", (GCallback) on_channel_added, sink); + g_signal_connect (priv->input, "channel-removed", (GCallback) on_channel_removed, sink); + + setup_pipeline (sink, NULL); } static void @@ -504,7 +427,7 @@ static void pinos_gst_sink_class_init (PinosGstSinkClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - PinosSinkClass *sink_class = PINOS_SINK_CLASS (klass); + PinosNodeClass *node_class = PINOS_NODE_CLASS (klass); g_type_class_add_private (klass, sizeof (PinosGstSinkPrivate)); @@ -532,10 +455,7 @@ pinos_gst_sink_class_init (PinosGstSinkClass * klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - sink_class->get_formats = get_formats; - sink_class->set_state = set_state; - sink_class->create_channel = create_channel; - sink_class->release_channel = release_channel; + node_class->set_state = set_state; } static void @@ -547,22 +467,22 @@ pinos_gst_sink_init (PinosGstSink * sink) priv->props = pinos_properties_new (NULL, NULL); } -PinosSink * -pinos_gst_sink_new (PinosNode *node, +PinosNode * +pinos_gst_sink_new (PinosDaemon *daemon, const gchar *name, PinosProperties *properties, GstElement *element, GstCaps *caps) { - PinosSink *sink; + PinosNode *node; - sink = g_object_new (PINOS_TYPE_GST_SINK, - "node", node, + node = g_object_new (PINOS_TYPE_GST_SINK, + "daemon", daemon, "name", name, "properties", properties, "element", element, "possible-formats", caps, NULL); - return sink; + return node; } diff --git a/pinos/modules/gst/gst-sink.h b/pinos/modules/gst/gst-sink.h index 72b4d7ce5..414739d7b 100644 --- a/pinos/modules/gst/gst-sink.h +++ b/pinos/modules/gst/gst-sink.h @@ -24,7 +24,6 @@ #include #include -#include G_BEGIN_DECLS @@ -42,18 +41,18 @@ typedef struct _PinosGstSinkClass PinosGstSinkClass; typedef struct _PinosGstSinkPrivate PinosGstSinkPrivate; struct _PinosGstSink { - PinosSink object; + PinosNode object; PinosGstSinkPrivate *priv; }; struct _PinosGstSinkClass { - PinosSinkClass parent_class; + PinosNodeClass parent_class; }; GType pinos_gst_sink_get_type (void); -PinosSink * pinos_gst_sink_new (PinosNode *node, +PinosNode * pinos_gst_sink_new (PinosDaemon *daemon, const gchar *name, PinosProperties *properties, GstElement *element, diff --git a/pinos/modules/gst/gst-source.c b/pinos/modules/gst/gst-source.c index dea985f61..8a6c708c1 100644 --- a/pinos/modules/gst/gst-source.c +++ b/pinos/modules/gst/gst-source.c @@ -35,6 +35,7 @@ struct _PinosGstSourcePrivate GstElement *filter; GstElement *sink; + PinosPort *output; GstCaps *possible_formats; GstNetTimeProvider *provider; @@ -50,15 +51,15 @@ enum { PROP_POSSIBLE_FORMATS }; -G_DEFINE_TYPE (PinosGstSource, pinos_gst_source, PINOS_TYPE_SOURCE); +G_DEFINE_TYPE (PinosGstSource, pinos_gst_source, PINOS_TYPE_NODE); static gboolean bus_handler (GstBus *bus, GstMessage *message, gpointer user_data) { - PinosSource *source = user_data; - PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (source)->priv; + PinosNode *node = user_data; + PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (node)->priv; switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_ERROR: @@ -70,7 +71,7 @@ bus_handler (GstBus *bus, g_warning ("got error %s (%s)\n", error->message, debug); g_free (debug); - pinos_source_report_error (source, error); + pinos_node_report_error (node, error); gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; } @@ -82,9 +83,9 @@ bus_handler (GstBus *bus, gst_message_parse_new_clock (message, &clock); GST_INFO ("got new clock %s", GST_OBJECT_NAME (clock)); - g_object_get (source, "properties", &props, NULL); + g_object_get (node, "properties", &props, NULL); pinos_properties_set (props, "gst.pipeline.clock", GST_OBJECT_NAME (clock)); - g_object_set (source, "properties", props, NULL); + g_object_set (node, "properties", props, NULL); pinos_properties_free (props); break; } @@ -96,9 +97,9 @@ bus_handler (GstBus *bus, gst_message_parse_new_clock (message, &clock); GST_INFO ("clock lost %s", GST_OBJECT_NAME (clock)); - g_object_get (source, "properties", &props, NULL); + g_object_get (node, "properties", &props, NULL); pinos_properties_remove (props, "gst.pipeline.clock"); - g_object_set (source, "properties", props, NULL); + g_object_set (node, "properties", props, NULL); pinos_properties_free (props); gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); @@ -111,19 +112,16 @@ bus_handler (GstBus *bus, return TRUE; } - - static gboolean setup_pipeline (PinosGstSource *source, GError **error) { PinosGstSourcePrivate *priv = source->priv; GstBus *bus; + g_debug ("gst-source %p: setup pipeline", source); priv->pipeline = gst_pipeline_new (NULL); gst_pipeline_set_latency (GST_PIPELINE_CAST (priv->pipeline), 0); - g_debug ("gst-source %p: setup pipeline", source); - gst_bin_add (GST_BIN (priv->pipeline), priv->element); priv->filter = gst_element_factory_make ("capsfilter", NULL); @@ -150,9 +148,10 @@ static gboolean start_pipeline (PinosGstSource *source, GError **error) { PinosGstSourcePrivate *priv = source->priv; - GstCaps *res; + GstCaps *caps; GstQuery *query; GstStateChangeReturn ret; + gchar *str; g_debug ("gst-source %p: starting pipeline", source); @@ -161,9 +160,13 @@ start_pipeline (PinosGstSource *source, GError **error) goto ready_failed; query = gst_query_new_caps (NULL); - gst_element_query (priv->element, query); - gst_query_parse_caps_result (query, &res); - gst_caps_replace (&priv->possible_formats, res); + if (gst_element_query (priv->element, query)) { + gst_query_parse_caps_result (query, &caps); + gst_caps_replace (&priv->possible_formats, caps); + str = gst_caps_to_string (caps); + g_debug ("gst-source %p: updated possible formats %s", source, str); + g_free (str); + } gst_query_unref (query); return TRUE; @@ -203,27 +206,27 @@ destroy_pipeline (PinosGstSource *source) static gboolean -set_state (PinosSource *source, - PinosSourceState state) +set_state (PinosNode *node, + PinosNodeState state) { - PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (source)->priv; + PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (node)->priv; - g_debug ("gst-source %p: set state %d", source, state); + g_debug ("gst-source %p: set state %s", node, pinos_node_state_as_string (state)); switch (state) { - case PINOS_SOURCE_STATE_SUSPENDED: + case PINOS_NODE_STATE_SUSPENDED: gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; - case PINOS_SOURCE_STATE_INITIALIZING: + case PINOS_NODE_STATE_INITIALIZING: gst_element_set_state (priv->pipeline, GST_STATE_READY); break; - case PINOS_SOURCE_STATE_IDLE: + case PINOS_NODE_STATE_IDLE: gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); break; - case PINOS_SOURCE_STATE_RUNNING: + case PINOS_NODE_STATE_RUNNING: { GstQuery *query; GstClock *clock; @@ -276,72 +279,13 @@ set_state (PinosSource *source, pinos_properties_setf (priv->props, "pinos.latency.max", "%"G_GUINT64_FORMAT, max_latency); break; } - case PINOS_SOURCE_STATE_ERROR: + case PINOS_NODE_STATE_ERROR: break; } - pinos_source_update_state (source, state); + pinos_node_update_state (node, state); return TRUE; } -static GBytes * -get_formats (PinosSource *source, - GBytes *filter, - GError **error) -{ - PinosGstSourcePrivate *priv = PINOS_GST_SOURCE (source)->priv; - GstCaps *caps, *cfilter; - gchar *str; - - if (filter) { - cfilter = gst_caps_from_string (g_bytes_get_data (filter, NULL)); - if (cfilter == NULL) - goto invalid_filter; - - caps = gst_caps_intersect (priv->possible_formats, cfilter); - gst_caps_unref (cfilter); - - if (caps == NULL) - goto no_formats; - - } else { - caps = gst_caps_ref (priv->possible_formats); - } - g_object_get (priv->filter, "caps", &cfilter, NULL); - if (cfilter != NULL) { - GstCaps *t = caps; - - caps = gst_caps_intersect (t, cfilter); - gst_caps_unref (cfilter); - gst_caps_unref (t); - } - if (gst_caps_is_empty (caps)) { - gst_caps_unref (caps); - goto no_formats; - } - - str = gst_caps_to_string (caps); - gst_caps_unref (caps); - - return g_bytes_new_take (str, strlen (str) + 1); - -invalid_filter: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Invalid filter received"); - return NULL; - } -no_formats: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "No compatible format found"); - return NULL; - } -} - static void on_socket_notify (GObject *gobject, GParamSpec *pspec, @@ -374,7 +318,7 @@ on_socket_notify (GObject *gobject, g_object_get (priv->sink, "num-handles", &num_handles, NULL); if (num_handles == 0) { - pinos_source_report_idle (PINOS_SOURCE (source)); + pinos_node_report_idle (PINOS_NODE (source)); g_object_set (priv->filter, "caps", NULL, NULL); str = gst_caps_to_string (priv->possible_formats); @@ -404,10 +348,10 @@ on_socket_notify (GObject *gobject, } /* this is what we use as the final format for the output */ g_object_set (gobject, "format", format, NULL); - pinos_source_report_busy (PINOS_SOURCE (source)); + pinos_node_report_busy (PINOS_NODE (source)); } if (format) { - pinos_source_update_possible_formats (PINOS_SOURCE (source), format); + g_object_set (priv->output, "possible-formats", format, NULL); g_bytes_unref (format); } @@ -420,64 +364,29 @@ on_socket_notify (GObject *gobject, pinos_properties_free (props); } -static PinosChannel * -create_channel (PinosSource *source, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error) +static void +on_channel_added (PinosPort *port, PinosChannel *channel, PinosGstSource *source) { - PinosGstSource *s = PINOS_GST_SOURCE (source); - PinosGstSourcePrivate *priv = s->priv; - PinosChannel *channel; - gpointer state = NULL; - const gchar *key, *val; + PinosGstSourcePrivate *priv = source->priv; + GError *error = NULL; if (priv->n_channels == 0) { - if (!start_pipeline (s, error)) - return NULL; + if (!start_pipeline (source, &error)) + return; } - while ((key = pinos_properties_iterate (priv->props, &state))) { - val = pinos_properties_get (priv->props, key); - pinos_properties_set (props, key, val); - } - - channel = PINOS_SOURCE_CLASS (pinos_gst_source_parent_class) - ->create_channel (source, - client_path, - format_filter, - props, - prefix, - error); - if (channel == NULL) - goto no_channel; - - g_signal_connect (channel, - "notify::socket", - (GCallback) on_socket_notify, - source); - + g_signal_connect (channel, "notify::socket", (GCallback) on_socket_notify, source); priv->n_channels++; - - return channel; - - /* ERRORS */ -no_channel: - { - if (priv->n_channels == 0) - stop_pipeline (s); - return NULL; - } } -static gboolean -release_channel (PinosSource *source, - PinosChannel *channel) +static void +on_channel_removed (PinosPort *port, PinosChannel *channel, PinosGstSource *source) { - return PINOS_SOURCE_CLASS (pinos_gst_source_parent_class) - ->release_channel (source, channel); + PinosGstSourcePrivate *priv = source->priv; + + priv->n_channels--; + if (priv->n_channels == 0) + stop_pipeline (source); } static void @@ -532,10 +441,26 @@ static void source_constructed (GObject * object) { PinosGstSource *source = PINOS_GST_SOURCE (object); - - setup_pipeline (source, NULL); + PinosGstSourcePrivate *priv = source->priv; + gchar *str; + GBytes *format; G_OBJECT_CLASS (pinos_gst_source_parent_class)->constructed (object); + + str = gst_caps_to_string (priv->possible_formats); + format = g_bytes_new_take (str, strlen (str) + 1); + + priv->output = pinos_port_new (PINOS_NODE (source), + PINOS_DIRECTION_OUTPUT, + "output", + format, + NULL); + g_bytes_unref (format); + + g_signal_connect (priv->output, "channel-added", (GCallback) on_channel_added, source); + g_signal_connect (priv->output, "channel-removed", (GCallback) on_channel_removed, source); + + setup_pipeline (source, NULL); } static void @@ -555,7 +480,7 @@ static void pinos_gst_source_class_init (PinosGstSourceClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - PinosSourceClass *source_class = PINOS_SOURCE_CLASS (klass); + PinosNodeClass *node_class = PINOS_NODE_CLASS (klass); g_type_class_add_private (klass, sizeof (PinosGstSourcePrivate)); @@ -583,10 +508,7 @@ pinos_gst_source_class_init (PinosGstSourceClass * klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - source_class->get_formats = get_formats; - source_class->set_state = set_state; - source_class->create_channel = create_channel; - source_class->release_channel = release_channel; + node_class->set_state = set_state; } static void @@ -598,22 +520,22 @@ pinos_gst_source_init (PinosGstSource * source) priv->props = pinos_properties_new (NULL, NULL); } -PinosSource * -pinos_gst_source_new (PinosNode *node, +PinosNode * +pinos_gst_source_new (PinosDaemon *daemon, const gchar *name, PinosProperties *properties, GstElement *element, GstCaps *caps) { - PinosSource *source; + PinosNode *node; - source = g_object_new (PINOS_TYPE_GST_SOURCE, - "node", node, - "name", name, - "properties", properties, - "element", element, - "possible-formats", caps, - NULL); + node = g_object_new (PINOS_TYPE_GST_SOURCE, + "daemon", daemon, + "name", name, + "properties", properties, + "element", element, + "possible-formats", caps, + NULL); - return source; + return node; } diff --git a/pinos/modules/gst/gst-source.h b/pinos/modules/gst/gst-source.h index db1ae8a04..4e3d8fc56 100644 --- a/pinos/modules/gst/gst-source.h +++ b/pinos/modules/gst/gst-source.h @@ -24,7 +24,6 @@ #include #include -#include G_BEGIN_DECLS @@ -42,18 +41,18 @@ typedef struct _PinosGstSourceClass PinosGstSourceClass; typedef struct _PinosGstSourcePrivate PinosGstSourcePrivate; struct _PinosGstSource { - PinosSource object; + PinosNode object; PinosGstSourcePrivate *priv; }; struct _PinosGstSourceClass { - PinosSourceClass parent_class; + PinosNodeClass parent_class; }; GType pinos_gst_source_get_type (void); -PinosSource * pinos_gst_source_new (PinosNode *node, +PinosNode * pinos_gst_source_new (PinosDaemon *daemon, const gchar *name, PinosProperties *properties, GstElement *element, diff --git a/pinos/server/channel.c b/pinos/server/channel.c index aa2ef92ee..9e03606e2 100644 --- a/pinos/server/channel.c +++ b/pinos/server/channel.c @@ -36,7 +36,8 @@ struct _PinosChannelPrivate gchar *object_path; gchar *client_path; - gchar *owner_path; + gchar *port_path; + PinosDirection direction; GBytes *possible_formats; PinosProperties *properties; @@ -58,7 +59,8 @@ enum PROP_DAEMON, PROP_OBJECT_PATH, PROP_CLIENT_PATH, - PROP_OWNER_PATH, + PROP_PORT_PATH, + PROP_DIRECTION, PROP_POSSIBLE_FORMATS, PROP_PROPERTIES, PROP_REQUESTED_FORMAT, @@ -97,8 +99,12 @@ pinos_channel_get_property (GObject *_object, g_value_set_string (value, priv->client_path); break; - case PROP_OWNER_PATH: - g_value_set_string (value, priv->owner_path); + case PROP_PORT_PATH: + g_value_set_string (value, priv->port_path); + break; + + case PROP_DIRECTION: + g_value_set_enum (value, priv->direction); break; case PROP_POSSIBLE_FORMATS: @@ -154,9 +160,14 @@ pinos_channel_set_property (GObject *_object, g_object_set (priv->iface, "client", priv->client_path, NULL); break; - case PROP_OWNER_PATH: - priv->owner_path = g_value_dup_string (value); - g_object_set (priv->iface, "owner", priv->owner_path, NULL); + case PROP_PORT_PATH: + priv->port_path = g_value_dup_string (value); + g_object_set (priv->iface, "port", priv->port_path, NULL); + break; + + case PROP_DIRECTION: + priv->direction = g_value_get_enum (value); + g_object_set (priv->iface, "direction", priv->direction, NULL); break; case PROP_POSSIBLE_FORMATS: @@ -212,7 +223,7 @@ stop_transfer (PinosChannel *channel) g_object_notify (G_OBJECT (channel), "socket"); } clear_formats (channel); - priv->state = PINOS_CHANNEL_STATE_IDLE; + priv->state = PINOS_CHANNEL_STATE_STOPPED; g_object_set (priv->iface, "state", priv->state, NULL); @@ -321,14 +332,13 @@ handle_remove (PinosChannel1 *interface, } static void -channel_register_object (PinosChannel *channel, - const gchar *prefix) +channel_register_object (PinosChannel *channel) { PinosChannelPrivate *priv = channel->priv; PinosObjectSkeleton *skel; gchar *name; - name = g_strdup_printf ("%s/channel", prefix); + name = g_strdup_printf ("%s/channel", priv->client_path); skel = pinos_object_skeleton_new (name); g_free (name); @@ -377,7 +387,7 @@ pinos_channel_finalize (GObject * object) g_clear_object (&priv->iface); g_free (priv->client_path); g_free (priv->object_path); - g_free (priv->owner_path); + g_free (priv->port_path); G_OBJECT_CLASS (pinos_channel_parent_class)->finalize (object); } @@ -386,10 +396,9 @@ static void pinos_channel_constructed (GObject * object) { PinosChannel *channel = PINOS_CHANNEL (object); - PinosChannelPrivate *priv = channel->priv; g_debug ("channel %p: constructed", channel); - channel_register_object (channel, priv->object_path); + channel_register_object (channel); G_OBJECT_CLASS (pinos_channel_parent_class)->constructed (object); } @@ -438,15 +447,26 @@ pinos_channel_class_init (PinosChannelClass * klass) G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, - PROP_OWNER_PATH, - g_param_spec_string ("owner-path", - "Owner Path", - "The owner object path", + PROP_PORT_PATH, + g_param_spec_string ("port-path", + "Port Path", + "The port object path", NULL, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | + G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_DIRECTION, + g_param_spec_enum ("direction", + "Direction", + "The direction of the port", + PINOS_TYPE_DIRECTION, + PINOS_DIRECTION_INVALID, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_POSSIBLE_FORMATS, g_param_spec_boxed ("possible-formats", @@ -516,9 +536,11 @@ pinos_channel_init (PinosChannel * channel) g_signal_connect (priv->iface, "handle-stop", (GCallback) handle_stop, channel); g_signal_connect (priv->iface, "handle-remove", (GCallback) handle_remove, channel); - priv->state = PINOS_CHANNEL_STATE_IDLE; + priv->state = PINOS_CHANNEL_STATE_STOPPED; g_object_set (priv->iface, "state", priv->state, NULL); + priv->direction = PINOS_DIRECTION_INVALID; + g_debug ("channel %p: new", channel); } diff --git a/pinos/server/client-source.h b/pinos/server/client-source.h deleted file mode 100644 index d38738210..000000000 --- a/pinos/server/client-source.h +++ /dev/null @@ -1,77 +0,0 @@ -/* Pinos - * Copyright (C) 2015 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __PINOS_CLIENT_SOURCE_H__ -#define __PINOS_CLIENT_SOURCE_H__ - -#include - -G_BEGIN_DECLS - -typedef struct _PinosClientSource PinosClientSource; -typedef struct _PinosClientSourceClass PinosClientSourceClass; -typedef struct _PinosClientSourcePrivate PinosClientSourcePrivate; - -#include - -#define PINOS_TYPE_CLIENT_SOURCE (pinos_client_source_get_type ()) -#define PINOS_IS_CLIENT_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SOURCE)) -#define PINOS_IS_CLIENT_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SOURCE)) -#define PINOS_CLIENT_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SOURCE, PinosClientSourceClass)) -#define PINOS_CLIENT_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SOURCE, PinosClientSource)) -#define PINOS_CLIENT_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SOURCE, PinosClientSourceClass)) -#define PINOS_CLIENT_SOURCE_CAST(obj) ((PinosClientSource*)(obj)) -#define PINOS_CLIENT_SOURCE_CLASS_CAST(klass) ((PinosClientSourceClass*)(klass)) - -/** - * PinosClientSource: - * - * Pinos client source object class. - */ -struct _PinosClientSource { - PinosSource object; - - PinosClientSourcePrivate *priv; -}; - -/** - * PinosClientSourceClass: - * - * Pinos client source object class. - */ -struct _PinosClientSourceClass { - PinosSourceClass parent_class; -}; - -/* normal GObject stuff */ -GType pinos_client_source_get_type (void); - -PinosSource * pinos_client_source_new (PinosDaemon *daemon, - GBytes *possible_formats); - -PinosChannel * pinos_client_source_get_channel (PinosClientSource *source, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error); - -G_END_DECLS - -#endif /* __PINOS_CLIENT_SOURCE_H__ */ diff --git a/pinos/server/client.c b/pinos/server/client.c index 01a9bdc1f..4c5bc39f6 100644 --- a/pinos/server/client.c +++ b/pinos/server/client.c @@ -21,7 +21,7 @@ #include "pinos/client/pinos.h" #include "pinos/server/client.h" -#include "pinos/server/client-source.h" +#include "pinos/server/upload-node.h" #include "pinos/dbus/org-pinos.h" @@ -139,17 +139,17 @@ handle_remove_channel (PinosChannel *channel, } static gboolean -handle_create_source_channel (PinosClient1 *interface, - GDBusMethodInvocation *invocation, - const gchar *arg_source, - const gchar *arg_accepted_formats, - GVariant *arg_properties, - gpointer user_data) +handle_create_channel (PinosClient1 *interface, + GDBusMethodInvocation *invocation, + PinosDirection direction, + const gchar *arg_port, + const gchar *arg_possible_formats, + GVariant *arg_properties, + gpointer user_data) { PinosClient *client = user_data; PinosClientPrivate *priv = client->priv; - PinosNode *node; - PinosSource *source; + PinosPort *port; PinosChannel *channel; const gchar *object_path, *sender; GBytes *formats; @@ -160,27 +160,25 @@ handle_create_source_channel (PinosClient1 *interface, if (g_strcmp0 (pinos_client_get_sender (client), sender) != 0) goto not_allowed; - formats = g_bytes_new (arg_accepted_formats, strlen (arg_accepted_formats) + 1); + formats = g_bytes_new (arg_possible_formats, strlen (arg_possible_formats) + 1); props = pinos_properties_from_variant (arg_properties); - node = pinos_daemon_find_node (priv->daemon, - arg_source, + port = pinos_daemon_find_port (priv->daemon, + direction, + arg_port, props, formats, &error); - if (node == NULL) - goto no_node; + if (port == NULL) + goto no_port; - source = pinos_node_get_source (node); - if (source == NULL) - goto no_source; + g_debug ("client %p: matched port %s", client, pinos_port_get_object_path (port)); - channel = pinos_source_create_channel (source, - priv->object_path, - formats, - props, - priv->object_path, - &error); + channel = pinos_port_create_channel (port, + priv->object_path, + formats, + props, + &error); pinos_properties_free (props); g_bytes_unref (formats); @@ -208,124 +206,18 @@ not_allowed: "org.pinos.Error", "not client owner"); return TRUE; } -no_node: +no_port: { - g_debug ("client %p: could not find node %s, %s", client, arg_source, error->message); + g_debug ("client %p: could not find port %s, %s", client, arg_port, error->message); g_dbus_method_invocation_return_gerror (invocation, error); pinos_properties_free (props); g_bytes_unref (formats); g_clear_error (&error); return TRUE; } -no_source: - { - g_debug ("client %p: node %s is not a source", client, arg_source); - g_dbus_method_invocation_return_dbus_error (invocation, - "org.pinos.Error", "not node is not a source"); - pinos_properties_free (props); - g_bytes_unref (formats); - return TRUE; - } no_channel: { - g_debug ("client %p: could not create source channel %s", client, error->message); - g_dbus_method_invocation_return_gerror (invocation, error); - g_clear_error (&error); - return TRUE; - } -} - -static gboolean -handle_create_sink_channel (PinosClient1 *interface, - GDBusMethodInvocation *invocation, - const gchar *arg_sink, - const gchar *arg_accepted_formats, - GVariant *arg_properties, - gpointer user_data) -{ - PinosClient *client = user_data; - PinosClientPrivate *priv = client->priv; - PinosNode *node; - PinosSink *sink; - PinosChannel *channel; - const gchar *object_path, *sender; - GBytes *formats; - PinosProperties *props; - GError *error = NULL; - - sender = g_dbus_method_invocation_get_sender (invocation); - if (g_strcmp0 (pinos_client_get_sender (client), sender) != 0) - goto not_allowed; - - formats = g_bytes_new (arg_accepted_formats, strlen (arg_accepted_formats) + 1); - props = pinos_properties_from_variant (arg_properties); - - node = pinos_daemon_find_node (priv->daemon, - arg_sink, - props, - formats, - &error); - if (node == NULL) - goto no_node; - - sink = pinos_node_get_sink (node); - if (sink == NULL) - goto no_sink; - - channel = pinos_sink_create_channel (sink, - priv->object_path, - formats, - props, - priv->object_path, - &error); - pinos_properties_free (props); - g_bytes_unref (formats); - - if (channel == NULL) - goto no_channel; - - priv->channels = g_list_prepend (priv->channels, channel); - - g_signal_connect (channel, - "remove", - (GCallback) handle_remove_channel, - client); - - object_path = pinos_channel_get_object_path (channel); - g_debug ("client %p: add sink channel %p, %s", client, channel, object_path); - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(o)", object_path)); - - return TRUE; - - /* ERRORS */ -not_allowed: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.pinos.Error", "not client owner"); - return TRUE; - } -no_node: - { - g_debug ("client %p: could not find node %s, %s", client, arg_sink, error->message); - g_dbus_method_invocation_return_gerror (invocation, error); - pinos_properties_free (props); - g_bytes_unref (formats); - g_clear_error (&error); - return TRUE; - } -no_sink: - { - g_debug ("client %p: node %s is not a sink", client, arg_sink); - g_dbus_method_invocation_return_dbus_error (invocation, - "org.pinos.Error", "node is not a sink"); - pinos_properties_free (props); - g_bytes_unref (formats); - return TRUE; - } -no_channel: - { - g_debug ("client %p: could not create sink channel %s", client, error->message); + g_debug ("client %p: could not create channel %s", client, error->message); g_dbus_method_invocation_return_gerror (invocation, error); g_clear_error (&error); return TRUE; @@ -341,7 +233,7 @@ handle_create_upload_channel (PinosClient1 *interface, { PinosClient *client = user_data; PinosClientPrivate *priv = client->priv; - PinosSource *source; + PinosNode *node; PinosChannel *channel; const gchar *channel_path, *sender; GBytes *formats; @@ -354,19 +246,18 @@ handle_create_upload_channel (PinosClient1 *interface, formats = g_bytes_new (arg_possible_formats, strlen (arg_possible_formats) + 1); - source = pinos_client_source_new (priv->daemon, formats); - if (source == NULL) - goto no_source; + node = pinos_upload_node_new (priv->daemon, formats); + if (node == NULL) + goto no_node; sender = g_dbus_method_invocation_get_sender (invocation); props = pinos_properties_from_variant (arg_properties); - channel = pinos_client_source_get_channel (PINOS_CLIENT_SOURCE (source), - priv->object_path, - formats, - props, - priv->object_path, - &error); + channel = pinos_upload_node_get_channel (PINOS_UPLOAD_NODE (node), + priv->object_path, + formats, + props, + &error); pinos_properties_free (props); if (channel == NULL) @@ -374,7 +265,7 @@ handle_create_upload_channel (PinosClient1 *interface, g_object_set_data_full (G_OBJECT (channel), "channel-owner", - source, + node, g_object_unref); channel_path = pinos_channel_get_object_path (channel); @@ -399,11 +290,11 @@ not_allowed: "org.pinos.Error", "not client owner"); return TRUE; } -no_source: +no_node: { - g_debug ("client %p: could not create source", client); + g_debug ("client %p: could not create upload node", client); g_dbus_method_invocation_return_dbus_error (invocation, - "org.pinos.Error", "Can't create source"); + "org.pinos.Error", "Can't create upload node"); g_bytes_unref (formats); return TRUE; } @@ -411,7 +302,7 @@ no_channel: { g_debug ("client %p: could not create upload channel %s", client, error->message); g_dbus_method_invocation_return_gerror (invocation, error); - g_object_unref (source); + g_object_unref (node); g_clear_error (&error); g_bytes_unref (formats); return TRUE; @@ -448,11 +339,8 @@ client_register_object (PinosClient *client, priv->client1 = pinos_client1_skeleton_new (); pinos_client1_set_name (priv->client1, priv->sender); pinos_client1_set_properties (priv->client1, pinos_properties_to_variant (priv->properties)); - g_signal_connect (priv->client1, "handle-create-source-channel", - (GCallback) handle_create_source_channel, - client); - g_signal_connect (priv->client1, "handle-create-sink-channel", - (GCallback) handle_create_sink_channel, + g_signal_connect (priv->client1, "handle-create-channel", + (GCallback) handle_create_channel, client); g_signal_connect (priv->client1, "handle-create-upload-channel", (GCallback) handle_create_upload_channel, diff --git a/pinos/server/daemon.c b/pinos/server/daemon.c index 48188cfd4..7ccedb486 100644 --- a/pinos/server/daemon.c +++ b/pinos/server/daemon.c @@ -17,6 +17,8 @@ * Boston, MA 02110-1301, USA. */ +#include + #include #include "config.h" @@ -380,47 +382,83 @@ pinos_daemon_remove_node (PinosDaemon *daemon, } /** - * pinos_daemon_find_node: + * pinos_daemon_find_port: * @daemon: a #PinosDaemon - * @name: a node name - * @props: node properties + * @name: a port name + * @props: port properties * @format_filter: a format filter * @error: location for an error * - * Find the best node in @daemon that matches the given parameters. + * Find the best port in @daemon that matches the given parameters. * - * Returns: a #PinosNode or %NULL when no node could be found. + * Returns: a #PinosPort or %NULL when no port could be found. */ -PinosNode * -pinos_daemon_find_node (PinosDaemon *daemon, +PinosPort * +pinos_daemon_find_port (PinosDaemon *daemon, + PinosDirection direction, const gchar *name, PinosProperties *props, GBytes *format_filter, GError **error) { PinosDaemonPrivate *priv; - PinosNode *best = NULL; - GList *walk; + PinosPort *best = NULL; + GList *node, *port; + gboolean have_name; g_return_val_if_fail (PINOS_IS_DAEMON (daemon), NULL); priv = daemon->priv; - for (walk = priv->nodes; walk; walk = g_list_next (walk)) { - PinosNode *n = walk->data; + have_name = name ? strlen (name) > 0 : FALSE; - if (name == NULL) { - best = n; - break; + for (node = priv->nodes; node; node = g_list_next (node)) { + PinosNode *n = node->data; + gboolean node_found = FALSE; + + /* we found the node */ + if (have_name && g_str_has_suffix (pinos_node_get_object_path (n), name)) { + g_debug ("name \"%s\" matches node %s", name, pinos_node_get_object_path (n)); + node_found = TRUE; } - else if (g_str_has_suffix (pinos_node_get_object_path (n), name)) - best = n; - } + for (port = pinos_node_get_ports (n); port; port = g_list_next (port)) { + PinosPort *p = port->data; + PinosDirection dir; + GBytes *format; + + g_object_get (p, "direction", &dir, NULL); + if (dir != direction) + continue; + + if (have_name && !node_found) { + if (!g_str_has_suffix (pinos_port_get_object_path (p), name)) + continue; + g_debug ("name \"%s\" matches port %s", name, pinos_port_get_object_path (p)); + best = p; + node_found = TRUE; + break; + } + + format = pinos_port_get_formats (p, format_filter, NULL); + if (format != NULL) { + g_debug ("port %s with format %s matches filter %s", + pinos_port_get_object_path (p), + (gchar*)g_bytes_get_data (format, NULL), + format_filter ? (gchar*)g_bytes_get_data (format_filter, NULL) : "ANY"); + g_bytes_unref (format); + best = p; + node_found = TRUE; + break; + } + } + if (node_found) + break; + } if (best == NULL) { if (error) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "Node not found"); + "No matching Port found"); } return best; } diff --git a/pinos/server/daemon.h b/pinos/server/daemon.h index 7be3e5bc3..31765bc82 100644 --- a/pinos/server/daemon.h +++ b/pinos/server/daemon.h @@ -38,6 +38,7 @@ typedef struct _PinosDaemonClass PinosDaemonClass; typedef struct _PinosDaemonPrivate PinosDaemonPrivate; #include +#include #include /** @@ -73,7 +74,9 @@ void pinos_daemon_unexport (PinosDaemon *daemon, const gch void pinos_daemon_add_node (PinosDaemon *daemon, PinosNode *node); void pinos_daemon_remove_node (PinosDaemon *daemon, PinosNode *node); -PinosNode * pinos_daemon_find_node (PinosDaemon *daemon, + +PinosPort * pinos_daemon_find_port (PinosDaemon *daemon, + PinosDirection direction, const gchar *name, PinosProperties *props, GBytes *format_filter, diff --git a/pinos/server/node.c b/pinos/server/node.c index 2ad9b4ec5..b06de8fee 100644 --- a/pinos/server/node.c +++ b/pinos/server/node.c @@ -23,7 +23,6 @@ #include "pinos/client/enumtypes.h" #include "pinos/server/node.h" -#include "pinos/server/source.h" #include "pinos/server/daemon.h" #include "pinos/dbus/org-pinos.h" @@ -35,10 +34,18 @@ struct _PinosNodePrivate { PinosDaemon *daemon; - PinosObjectSkeleton *skeleton; + PinosNode1 *iface; + gchar *object_path; - PinosSource *source; - PinosSink *sink; + gchar *name; + + PinosNodeState state; + GError *error; + guint idle_timeout; + + PinosProperties *properties; + + GList *ports; }; G_DEFINE_TYPE (PinosNode, pinos_node, G_TYPE_OBJECT); @@ -47,8 +54,10 @@ enum { PROP_0, PROP_DAEMON, - PROP_SKELETON, PROP_OBJECT_PATH, + PROP_NAME, + PROP_STATE, + PROP_PROPERTIES, }; static void @@ -65,14 +74,22 @@ pinos_node_get_property (GObject *_object, g_value_set_object (value, priv->daemon); break; - case PROP_SKELETON: - g_value_set_object (value, priv->skeleton); - break; - case PROP_OBJECT_PATH: g_value_set_string (value, priv->object_path); break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_STATE: + g_value_set_enum (value, priv->state); + break; + + case PROP_PROPERTIES: + g_value_set_boxed (value, priv->properties); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (node, prop_id, pspec); break; @@ -93,6 +110,19 @@ pinos_node_set_property (GObject *_object, priv->daemon = g_value_dup_object (value); break; + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; + + case PROP_PROPERTIES: + if (priv->properties) + pinos_properties_free (priv->properties); + priv->properties = g_value_dup_boxed (value); + if (priv->iface) + pinos_node1_set_properties (priv->iface, + priv->properties ? pinos_properties_to_variant (priv->properties) : NULL); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (node, prop_id, pspec); break; @@ -104,11 +134,20 @@ node_register_object (PinosNode *node) { PinosNodePrivate *priv = node->priv; PinosDaemon *daemon = priv->daemon; + PinosObjectSkeleton *skel; - priv->skeleton = pinos_object_skeleton_new (PINOS_DBUS_OBJECT_NODE); + skel = pinos_object_skeleton_new (PINOS_DBUS_OBJECT_NODE); + + priv->iface = pinos_node1_skeleton_new (); + pinos_node1_set_name (priv->iface, priv->name); + if (priv->properties) + pinos_node1_set_properties (priv->iface, pinos_properties_to_variant (priv->properties)); + pinos_node1_set_state (priv->iface, priv->state); + pinos_object_skeleton_set_node1 (skel, priv->iface); g_free (priv->object_path); - priv->object_path = pinos_daemon_export_uniquely (daemon, G_DBUS_OBJECT_SKELETON (priv->skeleton)); + priv->object_path = pinos_daemon_export_uniquely (daemon, G_DBUS_OBJECT_SKELETON (skel)); + g_object_unref (skel); pinos_daemon_add_node (daemon, node); @@ -122,7 +161,7 @@ node_unregister_object (PinosNode *node) pinos_daemon_unexport (priv->daemon, priv->object_path); pinos_daemon_remove_node (priv->daemon, node); - g_clear_object (&priv->skeleton); + g_clear_object (&priv->iface); } static void @@ -187,6 +226,37 @@ pinos_node_class_init (PinosNodeClass * klass) NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "The node name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_STATE, + g_param_spec_enum ("state", + "State", + "The state of the node", + PINOS_TYPE_NODE_STATE, + PINOS_NODE_STATE_SUSPENDED, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_PROPERTIES, + g_param_spec_boxed ("properties", + "Properties", + "The properties of the node", + PINOS_TYPE_PROPERTIES, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + } static void @@ -234,96 +304,210 @@ pinos_node_get_object_path (PinosNode *node) } /** - * pinos_node_set_source: + * pinos_node_add_port: * @node: a #PinosNode - * @source: a #PinosSource + * @port: a #PinosPort * - * Set the #PinosSource of @node + * Add the #PinosPort to @node */ void -pinos_node_set_source (PinosNode *node, PinosSource *source, GObject *iface) +pinos_node_add_port (PinosNode *node, PinosPort *port) { PinosNodePrivate *priv; g_return_if_fail (PINOS_IS_NODE (node)); - g_return_if_fail (source == NULL || PINOS_IS_SOURCE (source)); - g_return_if_fail (iface == NULL || PINOS_IS_SOURCE1 (iface)); + g_return_if_fail (PINOS_IS_PORT (port)); priv = node->priv; - if (source) { - pinos_object_skeleton_set_source1 (priv->skeleton, PINOS_SOURCE1 (iface)); - priv->source = source; - } else { - pinos_object_skeleton_set_source1 (priv->skeleton, NULL); - priv->source = NULL; - } + priv->ports = g_list_append (priv->ports, port); } /** - * pinos_node_get_source: + * pinos_node_remove_port: + * @node: a #PinosNode + * @port: a #PinosPort + * + * Remove the #PinosPort from @node + */ +void +pinos_node_remove_port (PinosNode *node, PinosPort *port) +{ + PinosNodePrivate *priv; + + g_return_if_fail (PINOS_IS_NODE (node)); + g_return_if_fail (PINOS_IS_PORT (port)); + priv = node->priv; + + priv->ports = g_list_remove (priv->ports, port); +} + +/** + * pinos_node_get_ports: * @node: a #PinosNode * - * Get the #PinosSource of @node + * Get the list of ports in @node. * - * Returns: the #PinosSource of @node or %NULL + * Returns: a #GList of nodes owned by @node. */ -PinosSource * -pinos_node_get_source (PinosNode *node) +GList * +pinos_node_get_ports (PinosNode *node) { PinosNodePrivate *priv; g_return_val_if_fail (PINOS_IS_NODE (node), NULL); priv = node->priv; - return priv->source; + return priv->ports; } - -/** - * pinos_node_set_sink: - * @node: a #PinosNode - * @sink: a #PinosSink - * - * Set the #PinosSink of @node - */ -void -pinos_node_set_sink (PinosNode *node, PinosSink *sink, GObject *iface) +static void +remove_idle_timeout (PinosNode *node) { - PinosNodePrivate *priv; + PinosNodePrivate *priv = node->priv; - g_return_if_fail (PINOS_IS_NODE (node)); - g_return_if_fail (sink == NULL || PINOS_IS_SINK (sink)); - g_return_if_fail (iface == NULL || PINOS_IS_SINK1 (iface)); - priv = node->priv; - - if (sink) { - pinos_object_skeleton_set_sink1 (priv->skeleton, PINOS_SINK1 (iface)); - priv->sink = sink; - } else { - pinos_object_skeleton_set_sink1 (priv->skeleton, NULL); - priv->sink = NULL; + if (priv->idle_timeout) { + g_source_remove (priv->idle_timeout); + priv->idle_timeout = 0; } } /** - * pinos_node_get_sink: + * pinos_node_set_state: * @node: a #PinosNode + * @state: a #PinosNodeState * - * Get the #PinosSink of @node + * Set the state of @node to @state. * - * Returns: the #PinosSink of @node or %NULL + * Returns: %TRUE on success. */ -PinosSink * -pinos_node_get_sink (PinosNode *node) +gboolean +pinos_node_set_state (PinosNode *node, + PinosNodeState state) +{ + PinosNodeClass *klass; + gboolean res; + + g_return_val_if_fail (PINOS_IS_NODE (node), FALSE); + + klass = PINOS_NODE_GET_CLASS (node); + + remove_idle_timeout (node); + + if (klass->set_state) + res = klass->set_state (node, state); + else + res = FALSE; + + return res; +} + +/** + * pinos_node_update_state: + * @node: a #PinosNode + * @state: a #PinosNodeState + * + * Update the state of a node. This method is used from + * inside @node itself. + */ +void +pinos_node_update_state (PinosNode *node, + PinosNodeState state) { PinosNodePrivate *priv; - g_return_val_if_fail (PINOS_IS_NODE (node), NULL); + g_return_if_fail (PINOS_IS_NODE (node)); priv = node->priv; - return priv->sink; + if (priv->state != state) { + priv->state = state; + pinos_node1_set_state (priv->iface, state); + g_object_notify (G_OBJECT (node), "state"); + } } +/** + * pinos_node_report_error: + * @node: a #PinosNode + * @error: a #GError + * + * Report an error from within @node. + */ +void +pinos_node_report_error (PinosNode *node, + GError *error) +{ + PinosNodePrivate *priv; + + g_return_if_fail (PINOS_IS_NODE (node)); + priv = node->priv; + + g_clear_error (&priv->error); + remove_idle_timeout (node); + priv->error = error; + priv->state = PINOS_NODE_STATE_ERROR; + g_debug ("got error state %s", error->message); + pinos_node1_set_state (priv->iface, priv->state); + g_object_notify (G_OBJECT (node), "state"); +} + +static gboolean +idle_timeout (PinosNode *node) +{ + PinosNodePrivate *priv = node->priv; + + priv->idle_timeout = 0; + pinos_node_set_state (node, PINOS_NODE_STATE_SUSPENDED); + + return G_SOURCE_REMOVE; +} + +/** + * pinos_node_report_idle: + * @node: a #PinosNode + * + * Mark @node as being idle. This will start a timeout that will + * set the node to SUSPENDED. + */ +void +pinos_node_report_idle (PinosNode *node) +{ + PinosNodePrivate *priv; + + g_return_if_fail (PINOS_IS_NODE (node)); + priv = node->priv; + + pinos_node_set_state (node, PINOS_NODE_STATE_IDLE); + + priv->idle_timeout = g_timeout_add_seconds (3, + (GSourceFunc) idle_timeout, + node); +} + +/** + * pinos_node_report_busy: + * @node: a #PinosNode + * + * Mark @node as being busy. This will set the state of the node + * to the RUNNING state. + */ +void +pinos_node_report_busy (PinosNode *node) +{ + g_return_if_fail (PINOS_IS_NODE (node)); + + pinos_node_set_state (node, PINOS_NODE_STATE_RUNNING); +} + + + +/** + * pinos_node_new: + * @daemon: a #PinosDaemon + * + * Make a new node + * + * Returns: a new #PinosNode + */ PinosNode * pinos_node_new (PinosDaemon *daemon) { diff --git a/pinos/server/node.h b/pinos/server/node.h index cbdb21b4d..69b6b789f 100644 --- a/pinos/server/node.h +++ b/pinos/server/node.h @@ -30,8 +30,8 @@ typedef struct _PinosNodePrivate PinosNodePrivate; #include #include -#include -#include +#include +#include #define PINOS_TYPE_NODE (pinos_node_get_type ()) #define PINOS_IS_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_NODE)) @@ -55,11 +55,15 @@ struct _PinosNode { /** * PinosNodeClass: + * @set_state: called to change the current state of the node * * Pinos node class. */ struct _PinosNodeClass { GObjectClass parent_class; + + gboolean (*set_state) (PinosNode *node, PinosNodeState state); + }; /* normal GObject stuff */ @@ -70,15 +74,18 @@ PinosNode * pinos_node_new (PinosDaemon *daemon); PinosDaemon * pinos_node_get_daemon (PinosNode *node); const gchar * pinos_node_get_object_path (PinosNode *node); -void pinos_node_set_source (PinosNode *node, - PinosSource *source, - GObject *iface); -PinosSource * pinos_node_get_source (PinosNode *node); +void pinos_node_add_port (PinosNode *node, + PinosPort *port); +void pinos_node_remove_port (PinosNode *node, + PinosPort *port); +GList * pinos_node_get_ports (PinosNode *node); -void pinos_node_set_sink (PinosNode *node, - PinosSink *sink, - GObject *iface); -PinosSink * pinos_node_get_sink (PinosNode *node); +gboolean pinos_node_set_state (PinosNode *node, PinosNodeState state); +void pinos_node_update_state (PinosNode *node, PinosNodeState state); + +void pinos_node_report_error (PinosNode *node, GError *error); +void pinos_node_report_idle (PinosNode *node); +void pinos_node_report_busy (PinosNode *node); G_END_DECLS diff --git a/pinos/server/port.c b/pinos/server/port.c new file mode 100644 index 000000000..93b757273 --- /dev/null +++ b/pinos/server/port.c @@ -0,0 +1,666 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include "pinos/client/pinos.h" +#include "pinos/client/enumtypes.h" + +#include "pinos/server/port.h" +#include "pinos/server/node.h" + +#include "pinos/dbus/org-pinos.h" + +#define PINOS_PORT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_PORT, PinosPortPrivate)) + +struct _PinosPortPrivate +{ + PinosNode *node; + PinosPort1 *iface; + gchar *object_path; + + gchar *name; + PinosDirection direction; + GBytes *possible_formats; + PinosProperties *properties; + + GList *channels; +}; + +G_DEFINE_TYPE (PinosPort, pinos_port, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_NODE, + PROP_OBJECT_PATH, + PROP_NAME, + PROP_DIRECTION, + PROP_POSSIBLE_FORMATS, + PROP_PROPERTIES +}; + +enum +{ + SIGNAL_FORMAT_REQUEST, + SIGNAL_CHANNEL_ADDED, + SIGNAL_CHANNEL_REMOVED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +pinos_port_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PinosPort *port = PINOS_PORT (_object); + PinosPortPrivate *priv = port->priv; + + switch (prop_id) { + case PROP_NODE: + g_value_set_object (value, priv->node); + break; + + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, priv->object_path); + break; + + case PROP_DIRECTION: + g_value_set_enum (value, priv->direction); + break; + + case PROP_POSSIBLE_FORMATS: + g_value_set_boxed (value, priv->possible_formats); + break; + + case PROP_PROPERTIES: + g_value_set_boxed (value, priv->properties); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (port, prop_id, pspec); + break; + } +} + +static void +set_possible_formats (PinosPort *port, GBytes *formats) +{ + PinosPortPrivate *priv; + GList *walk; + + g_return_if_fail (PINOS_IS_PORT (port)); + priv = port->priv; + + if (priv->possible_formats) + g_bytes_unref (priv->possible_formats); + priv->possible_formats = formats ? g_bytes_ref (formats) : NULL; + + if (priv->iface) + g_object_set (priv->iface, "possible-formats", + g_bytes_get_data (formats, NULL), + NULL); + + for (walk = priv->channels; walk; walk = g_list_next (walk)) + g_object_set (walk->data, "possible-formats", formats, NULL); +} + +static void +pinos_port_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PinosPort *port = PINOS_PORT (_object); + PinosPortPrivate *priv = port->priv; + + switch (prop_id) { + case PROP_NODE: + priv->node = g_value_dup_object (value); + break; + + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; + + case PROP_DIRECTION: + priv->direction = g_value_get_enum (value); + break; + + case PROP_POSSIBLE_FORMATS: + set_possible_formats (port, g_value_get_boxed (value)); + break; + + case PROP_PROPERTIES: + if (priv->properties) + pinos_properties_free (priv->properties); + priv->properties = g_value_dup_boxed (value); + if (priv->iface) + g_object_set (priv->iface, + "properties", priv->properties ? + pinos_properties_to_variant (priv->properties) : NULL, + NULL); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (port, prop_id, pspec); + break; + } +} + +static void +port_register_object (PinosPort *port) +{ + PinosPortPrivate *priv = port->priv; + GBytes *formats; + GVariant *variant; + PinosObjectSkeleton *skel; + gchar *name; + + name = g_strdup_printf ("%s/port", pinos_node_get_object_path (priv->node)); + skel = pinos_object_skeleton_new (name); + g_free (name); + + formats = pinos_port_get_formats (port, NULL, NULL); + + if (priv->properties) + variant = pinos_properties_to_variant (priv->properties); + else + variant = NULL; + + priv->iface = pinos_port1_skeleton_new (); + g_object_set (priv->iface, "name", priv->name, + "node", pinos_node_get_object_path (priv->node), + "direction", priv->direction, + "properties", variant, + "possible-formats", g_bytes_get_data (formats, NULL), + NULL); + g_bytes_unref (formats); + pinos_object_skeleton_set_port1 (skel, priv->iface); + + g_free (priv->object_path); + priv->object_path = pinos_daemon_export_uniquely (pinos_node_get_daemon (priv->node), + G_DBUS_OBJECT_SKELETON (skel)); + + g_object_unref (skel); + + pinos_node_add_port (priv->node, port); + + return; +} + +static void +port_unregister_object (PinosPort *port) +{ + PinosPortPrivate *priv = port->priv; + + pinos_node_remove_port (priv->node, port); + g_clear_object (&priv->iface); +} + +static void +pinos_port_constructed (GObject * object) +{ + PinosPort *port = PINOS_PORT (object); + + port_register_object (port); + + G_OBJECT_CLASS (pinos_port_parent_class)->constructed (object); +} + +static void +do_remove_channel (PinosChannel *channel, + gpointer user_data) +{ + pinos_channel_remove (channel); +} + +static void +pinos_port_dispose (GObject * object) +{ + PinosPort *port = PINOS_PORT (object); + PinosPortPrivate *priv = port->priv; + + g_list_foreach (priv->channels, (GFunc) do_remove_channel, port); + port_unregister_object (port); + + G_OBJECT_CLASS (pinos_port_parent_class)->dispose (object); +} + +static void +pinos_port_finalize (GObject * object) +{ + PinosPort *port = PINOS_PORT (object); + PinosPortPrivate *priv = port->priv; + + g_free (priv->name); + if (priv->possible_formats) + g_bytes_unref (priv->possible_formats); + if (priv->properties) + pinos_properties_free (priv->properties); + + G_OBJECT_CLASS (pinos_port_parent_class)->finalize (object); +} + +static void +handle_remove_channel (PinosChannel *channel, + gpointer user_data) +{ + PinosPort *port = user_data; + + pinos_port_release_channel (port, channel); +} + +static PinosChannel * +default_create_channel (PinosPort *port, + const gchar *client_path, + GBytes *format_filter, + PinosProperties *props, + GError **error) +{ + PinosPortPrivate *priv = port->priv; + PinosChannel *channel; + GBytes *possible_formats; + + possible_formats = pinos_port_get_formats (port, format_filter, error); + if (possible_formats == NULL) + return NULL; + + channel = g_object_new (PINOS_TYPE_CHANNEL, "daemon", pinos_node_get_daemon (priv->node), + "client-path", client_path, + "direction", priv->direction, + "port-path", pinos_port_get_object_path (port), + "possible-formats", possible_formats, + "properties", props, + NULL); + g_bytes_unref (possible_formats); + + if (channel == NULL) + goto no_channel; + + g_signal_connect (channel, + "remove", + (GCallback) handle_remove_channel, + port); + + priv->channels = g_list_prepend (priv->channels, channel); + + return g_object_ref (channel); + + /* ERRORS */ +no_channel: + { + if (error) + *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_FAILED, + "Could not create channel"); + return NULL; + } +} + +static gboolean +default_release_channel (PinosPort *port, + PinosChannel *channel) +{ + PinosPortPrivate *priv = port->priv; + GList *find; + + find = g_list_find (priv->channels, channel); + if (find == NULL) + return FALSE; + + priv->channels = g_list_delete_link (priv->channels, find); + g_object_unref (channel); + + return TRUE; +} + +static void +pinos_port_class_init (PinosPortClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PinosPortPrivate)); + + gobject_class->constructed = pinos_port_constructed; + gobject_class->dispose = pinos_port_dispose; + gobject_class->finalize = pinos_port_finalize; + gobject_class->set_property = pinos_port_set_property; + gobject_class->get_property = pinos_port_get_property; + + g_object_class_install_property (gobject_class, + PROP_NODE, + g_param_spec_object ("node", + "Node", + "The Node", + PINOS_TYPE_NODE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "The port name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_DIRECTION, + g_param_spec_enum ("direction", + "Direction", + "The direction of the port", + PINOS_TYPE_DIRECTION, + PINOS_DIRECTION_INVALID, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_POSSIBLE_FORMATS, + g_param_spec_boxed ("possible-formats", + "Possible Formats", + "The possbile formats of the port", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_PROPERTIES, + g_param_spec_boxed ("properties", + "Properties", + "The properties of the port", + PINOS_TYPE_PROPERTIES, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + + signals[SIGNAL_FORMAT_REQUEST] = g_signal_new ("format-request", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 0, + G_TYPE_NONE); + signals[SIGNAL_CHANNEL_ADDED] = g_signal_new ("channel-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + PINOS_TYPE_CHANNEL); + signals[SIGNAL_CHANNEL_REMOVED] = g_signal_new ("channel-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + PINOS_TYPE_CHANNEL); + + + klass->create_channel = default_create_channel; + klass->release_channel = default_release_channel; +} + +static void +pinos_port_init (PinosPort * port) +{ + PinosPortPrivate *priv = port->priv = PINOS_PORT_GET_PRIVATE (port); + + priv->direction = PINOS_DIRECTION_INVALID; +} + +/** + * pinos_port_new: + * + * + * Returns: a new #PinosPort + */ +PinosPort * +pinos_port_new (PinosNode *node, + PinosDirection direction, + const gchar *name, + GBytes *possible_formats, + PinosProperties *props) +{ + g_return_val_if_fail (PINOS_IS_NODE (node), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (PINOS_TYPE_PORT, + "node", node, + "direction", direction, + "name", name, + "possible-formats", possible_formats, + "properties", props, + NULL); +} + +const gchar * +pinos_port_get_object_path (PinosPort *port) +{ + PinosPortPrivate *priv; + + g_return_val_if_fail (PINOS_IS_PORT (port), NULL); + priv = port->priv; + + return priv->object_path; +} + +/** + * pinos_port_get_formats: + * @port: a #PinosPort + * @filter: a #GBytes + * @error: a #GError or %NULL + * + * Get all the currently supported formats for @port and filter the + * results with @filter. + * + * Returns: the list of supported format. If %NULL is returned, @error will + * be set. + */ +GBytes * +pinos_port_get_formats (PinosPort *port, + GBytes *filter, + GError **error) +{ + GstCaps *tmp, *caps, *cfilter; + gchar *str; + PinosPortPrivate *priv; + + g_return_val_if_fail (PINOS_IS_PORT (port), NULL); + priv = port->priv; + + if (filter) { + cfilter = gst_caps_from_string (g_bytes_get_data (filter, NULL)); + if (cfilter == NULL) + goto invalid_filter; + } else { + cfilter = NULL; + } + + g_signal_emit (port, signals[SIGNAL_FORMAT_REQUEST], 0, NULL); + + if (priv->possible_formats) + caps = gst_caps_from_string (g_bytes_get_data (priv->possible_formats, NULL)); + else + caps = gst_caps_new_any (); + + if (caps && cfilter) { + tmp = gst_caps_intersect_full (caps, cfilter, GST_CAPS_INTERSECT_FIRST); + g_clear_pointer (&cfilter, gst_caps_unref); + gst_caps_take (&caps, tmp); + } + if (caps == NULL || gst_caps_is_empty (caps)) + goto no_format; + + str = gst_caps_to_string (caps); + gst_caps_unref (caps); + + return g_bytes_new_take (str, strlen (str) + 1); + +invalid_filter: + { + if (error) + *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Invalid filter received"); + return NULL; + } +no_format: + { + if (error) + *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "No compatible format found"); + if (cfilter) + gst_caps_unref (cfilter); + if (caps) + gst_caps_unref (caps); + return NULL; + } +} + + +/** + * pinos_port_get_channels: + * @port: a #PinosPort + * + * Get all the channels in @port. + * + * Returns: a #GList of #PinosChannel objects. + */ +GList * +pinos_port_get_channels (PinosPort *port) +{ + PinosPortPrivate *priv; + + g_return_val_if_fail (PINOS_IS_PORT (port), NULL); + priv = port->priv; + + return priv->channels; +} + +/** + * pinos_port_create_channel: + * @port: a #PinosPort + * @client_path: the client path + * @format_filter: a #GBytes + * @props: #PinosProperties + * @prefix: a prefix + * @error: a #GError or %NULL + * + * Create a new #PinosChannel for @port. + * + * Returns: a new #PinosChannel or %NULL, in wich case @error will contain + * more information about the error. + */ +PinosChannel * +pinos_port_create_channel (PinosPort *port, + const gchar *client_path, + GBytes *format_filter, + PinosProperties *props, + GError **error) +{ + PinosPortClass *klass; + PinosChannel *channel; + + g_return_val_if_fail (PINOS_IS_PORT (port), NULL); + + klass = PINOS_PORT_GET_CLASS (port); + + if (klass->create_channel) { + channel = klass->create_channel (port, client_path, format_filter, props, error); + if (channel) + g_signal_emit (port, signals[SIGNAL_CHANNEL_ADDED], 0, channel); + } else { + if (error) { + *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CreateChannel not implemented"); + } + channel = NULL; + } + + return channel; +} + +/** + * pinos_port_release_channel: + * @port: a #PinosPort + * @channel: a #PinosChannel + * + * Release the @channel in @port. + * + * Returns: %TRUE on success. + */ +gboolean +pinos_port_release_channel (PinosPort *port, + PinosChannel *channel) +{ + PinosPortClass *klass; + gboolean res; + + g_return_val_if_fail (PINOS_IS_PORT (port), FALSE); + g_return_val_if_fail (PINOS_IS_CHANNEL (channel), FALSE); + + klass = PINOS_PORT_GET_CLASS (port); + + if (klass->release_channel) { + g_signal_emit (port, signals[SIGNAL_CHANNEL_REMOVED], 0, channel); + res = klass->release_channel (port, channel); + } + else + res = FALSE; + + return res; +} diff --git a/pinos/server/port.h b/pinos/server/port.h new file mode 100644 index 000000000..d8faaf8c1 --- /dev/null +++ b/pinos/server/port.h @@ -0,0 +1,101 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PINOS_PORT_H__ +#define __PINOS_PORT_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _PinosPort PinosPort; +typedef struct _PinosPortClass PinosPortClass; +typedef struct _PinosPortPrivate PinosPortPrivate; + +#include +#include +#include + +#define PINOS_TYPE_PORT (pinos_port_get_type ()) +#define PINOS_IS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_PORT)) +#define PINOS_IS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_PORT)) +#define PINOS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_PORT, PinosPortClass)) +#define PINOS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_PORT, PinosPort)) +#define PINOS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_PORT, PinosPortClass)) +#define PINOS_PORT_CAST(obj) ((PinosPort*)(obj)) +#define PINOS_PORT_CLASS_CAST(klass) ((PinosPortClass*)(klass)) + +/** + * PinosPort: + * + * Pinos port object class. + */ +struct _PinosPort { + GObject object; + + PinosPortPrivate *priv; +}; + +/** + * PinosPortClass: + * @get_formats: called to get a list of supported formats from the port + * @create_channel: called to create a new channel object + * @release_channel: called to release a channel object + * + * Pinos port object class. + */ +struct _PinosPortClass { + GObjectClass parent_class; + + PinosChannel * (*create_channel) (PinosPort *port, + const gchar *client_path, + GBytes *format_filter, + PinosProperties *props, + GError **error); + gboolean (*release_channel) (PinosPort *port, + PinosChannel *channel); +}; + +/* normal GObject stuff */ +GType pinos_port_get_type (void); + +PinosPort * pinos_port_new (PinosNode *node, + PinosDirection direction, + const gchar *name, + GBytes *possible_formats, + PinosProperties *props); + +const gchar * pinos_port_get_object_path (PinosPort *port); + +GBytes * pinos_port_get_formats (PinosPort *port, + GBytes *filter, + GError **error); + +PinosChannel * pinos_port_create_channel (PinosPort *port, + const gchar *client_path, + GBytes *format_filter, + PinosProperties *props, + GError **error); +gboolean pinos_port_release_channel (PinosPort *port, + PinosChannel *channel); +GList * pinos_port_get_channels (PinosPort *port); + +G_END_DECLS + +#endif /* __PINOS_PORT_H__ */ diff --git a/pinos/server/sink.c b/pinos/server/sink.c deleted file mode 100644 index 006bfd080..000000000 --- a/pinos/server/sink.c +++ /dev/null @@ -1,646 +0,0 @@ -/* Pinos - * Copyright (C) 2015 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include - -#include "pinos/client/pinos.h" -#include "pinos/client/enumtypes.h" - -#include "pinos/server/sink.h" -#include "pinos/server/node.h" - -#include "pinos/dbus/org-pinos.h" - - -#define PINOS_SINK_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SINK, PinosSinkPrivate)) - -struct _PinosSinkPrivate -{ - PinosNode *node; - PinosSink1 *iface; - - gchar *name; - PinosProperties *properties; - - PinosSinkState state; - GError *error; - guint idle_timeout; - - GList *channels; -}; - -G_DEFINE_ABSTRACT_TYPE (PinosSink, pinos_sink, G_TYPE_OBJECT); - -enum -{ - PROP_0, - PROP_NODE, - PROP_NAME, - PROP_STATE, - PROP_PROPERTIES -}; - -static void -pinos_sink_get_property (GObject *_object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PinosSink *sink = PINOS_SINK (_object); - PinosSinkPrivate *priv = sink->priv; - - switch (prop_id) { - case PROP_NODE: - g_value_set_object (value, priv->node); - break; - - case PROP_NAME: - g_value_set_string (value, priv->name); - break; - - case PROP_STATE: - g_value_set_enum (value, priv->state); - break; - - case PROP_PROPERTIES: - g_value_set_boxed (value, priv->properties); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (sink, prop_id, pspec); - break; - } -} - -static void -pinos_sink_set_property (GObject *_object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PinosSink *sink = PINOS_SINK (_object); - PinosSinkPrivate *priv = sink->priv; - - switch (prop_id) { - case PROP_NODE: - priv->node = g_value_dup_object (value); - break; - - case PROP_NAME: - g_free (priv->name); - priv->name = g_value_dup_string (value); - break; - - case PROP_PROPERTIES: - if (priv->properties) - pinos_properties_free (priv->properties); - priv->properties = g_value_dup_boxed (value); - if (priv->iface) - g_object_set (priv->iface, - "properties", priv->properties ? - pinos_properties_to_variant (priv->properties) : NULL, - NULL); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (sink, prop_id, pspec); - break; - } -} - -static void -sink_register_object (PinosSink *sink) -{ - PinosSinkPrivate *priv = sink->priv; - GBytes *formats; - GVariant *variant; - - formats = pinos_sink_get_formats (sink, NULL, NULL); - - if (priv->properties) - variant = pinos_properties_to_variant (priv->properties); - else - variant = NULL; - - priv->iface = pinos_sink1_skeleton_new (); - g_object_set (priv->iface, "name", priv->name, - "state", priv->state, - "properties", variant, - "possible-formats", g_bytes_get_data (formats, NULL), - NULL); - g_bytes_unref (formats); - - pinos_node_set_sink (priv->node, sink, G_OBJECT (priv->iface)); - - return; -} - -static void -sink_unregister_object (PinosSink *sink) -{ - PinosSinkPrivate *priv = sink->priv; - - pinos_node_set_sink (priv->node, NULL, NULL); - g_clear_object (&priv->iface); -} - -static void -pinos_sink_constructed (GObject * object) -{ - PinosSink *sink = PINOS_SINK (object); - - sink_register_object (sink); - - G_OBJECT_CLASS (pinos_sink_parent_class)->constructed (object); -} - -static void -do_remove_channel (PinosChannel *channel, - gpointer user_data) -{ - pinos_channel_remove (channel); -} - -static void -pinos_sink_dispose (GObject * object) -{ - PinosSink *sink = PINOS_SINK (object); - PinosSinkPrivate *priv = sink->priv; - - g_list_foreach (priv->channels, (GFunc) do_remove_channel, sink); - sink_unregister_object (sink); - - G_OBJECT_CLASS (pinos_sink_parent_class)->dispose (object); -} - -static void -pinos_sink_finalize (GObject * object) -{ - PinosSink *sink = PINOS_SINK (object); - PinosSinkPrivate *priv = sink->priv; - - g_free (priv->name); - if (priv->properties) - pinos_properties_free (priv->properties); - - G_OBJECT_CLASS (pinos_sink_parent_class)->finalize (object); -} - -static gboolean -default_set_state (PinosSink *sink, - PinosSinkState state) -{ - pinos_sink_update_state (sink, state); - return TRUE; -} - -static void -handle_remove_channel (PinosChannel *channel, - gpointer user_data) -{ - PinosSink *sink = user_data; - - pinos_sink_release_channel (sink, channel); -} - -static PinosChannel * -default_create_channel (PinosSink *sink, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error) -{ - PinosSinkPrivate *priv = sink->priv; - PinosChannel *channel; - GBytes *possible_formats; - - possible_formats = pinos_sink_get_formats (sink, format_filter, error); - if (possible_formats == NULL) - return NULL; - - channel = g_object_new (PINOS_TYPE_CHANNEL, "daemon", pinos_node_get_daemon (priv->node), - "object-path", prefix, - "client-path", client_path, - "owner-path", pinos_node_get_object_path (priv->node), - "possible-formats", possible_formats, - "properties", props, - NULL); - g_bytes_unref (possible_formats); - - if (channel == NULL) - goto no_channel; - - g_signal_connect (channel, - "remove", - (GCallback) handle_remove_channel, - sink); - - priv->channels = g_list_prepend (priv->channels, channel); - - return g_object_ref (channel); - - /* ERRORS */ -no_channel: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_FAILED, - "Could not create channel"); - return NULL; - } -} - -static gboolean -default_release_channel (PinosSink *sink, - PinosChannel *channel) -{ - PinosSinkPrivate *priv = sink->priv; - GList *find; - - find = g_list_find (priv->channels, channel); - if (find == NULL) - return FALSE; - - priv->channels = g_list_delete_link (priv->channels, find); - g_object_unref (channel); - - return TRUE; -} - -static void -pinos_sink_class_init (PinosSinkClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (PinosSinkPrivate)); - - gobject_class->constructed = pinos_sink_constructed; - gobject_class->dispose = pinos_sink_dispose; - gobject_class->finalize = pinos_sink_finalize; - gobject_class->set_property = pinos_sink_set_property; - gobject_class->get_property = pinos_sink_get_property; - - g_object_class_install_property (gobject_class, - PROP_NODE, - g_param_spec_object ("node", - "Node", - "The Node", - PINOS_TYPE_NODE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_NAME, - g_param_spec_string ("name", - "Name", - "The sink name", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_STATE, - g_param_spec_enum ("state", - "State", - "The state of the sink", - PINOS_TYPE_SINK_STATE, - PINOS_SINK_STATE_SUSPENDED, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_PROPERTIES, - g_param_spec_boxed ("properties", - "Properties", - "The properties of the sink", - PINOS_TYPE_PROPERTIES, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - - klass->set_state = default_set_state; - klass->create_channel = default_create_channel; - klass->release_channel = default_release_channel; -} - -static void -pinos_sink_init (PinosSink * sink) -{ - PinosSinkPrivate *priv = sink->priv = PINOS_SINK_GET_PRIVATE (sink); - - priv->state = PINOS_SINK_STATE_SUSPENDED; -} - -/** - * pinos_sink_get_formats: - * @sink: a #PinosSink - * @filter: a #GBytes - * @error: a #GError or %NULL - * - * Get all the currently supported formats for @sink and filter the - * results with @filter. - * - * Returns: the list of supported format. If %NULL is returned, @error will - * be set. - */ -GBytes * -pinos_sink_get_formats (PinosSink *sink, - GBytes *filter, - GError **error) -{ - PinosSinkClass *klass; - GBytes *res; - - g_return_val_if_fail (PINOS_IS_SINK (sink), NULL); - - klass = PINOS_SINK_GET_CLASS (sink); - - if (klass->get_formats) - res = klass->get_formats (sink, filter, error); - else { - res = NULL; - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "Format query is not supported"); - } - return res; -} - -static void -remove_idle_timeout (PinosSink *sink) -{ - PinosSinkPrivate *priv = sink->priv; - - if (priv->idle_timeout) { - g_source_remove (priv->idle_timeout); - priv->idle_timeout = 0; - } -} - -/** - * pinos_sink_set_state: - * @sink: a #PinosSink - * @state: a #PinosSinkState - * - * Set the state of @sink to @state. - * - * Returns: %TRUE on success. - */ -gboolean -pinos_sink_set_state (PinosSink *sink, - PinosSinkState state) -{ - PinosSinkClass *klass; - gboolean res; - - g_return_val_if_fail (PINOS_IS_SINK (sink), FALSE); - - klass = PINOS_SINK_GET_CLASS (sink); - - remove_idle_timeout (sink); - - if (klass->set_state) - res = klass->set_state (sink, state); - else - res = FALSE; - - return res; -} - -/** - * pinos_sink_update_state: - * @sink: a #PinosSink - * @state: a #PinosSinkState - * - * Update the state of a sink. This method is used from - * inside @sink itself. - */ -void -pinos_sink_update_state (PinosSink *sink, - PinosSinkState state) -{ - PinosSinkPrivate *priv; - - g_return_if_fail (PINOS_IS_SINK (sink)); - priv = sink->priv; - - if (priv->state != state) { - priv->state = state; - pinos_sink1_set_state (priv->iface, state); - g_object_notify (G_OBJECT (sink), "state"); - } -} - -/** - * pinos_sink_report_error: - * @sink: a #PinosSink - * @error: a #GError - * - * Report an error from within @sink. - */ -void -pinos_sink_report_error (PinosSink *sink, - GError *error) -{ - PinosSinkPrivate *priv; - - g_return_if_fail (PINOS_IS_SINK (sink)); - priv = sink->priv; - - g_clear_error (&priv->error); - remove_idle_timeout (sink); - priv->error = error; - priv->state = PINOS_SINK_STATE_ERROR; - g_debug ("got error state %s", error->message); - pinos_sink1_set_state (priv->iface, priv->state); - g_object_notify (G_OBJECT (sink), "state"); -} - -static gboolean -idle_timeout (PinosSink *sink) -{ - PinosSinkPrivate *priv = sink->priv; - - priv->idle_timeout = 0; - pinos_sink_set_state (sink, PINOS_SINK_STATE_SUSPENDED); - - return G_SOURCE_REMOVE; -} - -/** - * pinos_sink_report_idle: - * @sink: a #PinosSink - * - * Mark @sink as being idle. This will start a timeout that will - * set the sink to SUSPENDED. - */ -void -pinos_sink_report_idle (PinosSink *sink) -{ - PinosSinkPrivate *priv; - - g_return_if_fail (PINOS_IS_SINK (sink)); - priv = sink->priv; - - pinos_sink_set_state (sink, PINOS_SINK_STATE_IDLE); - - priv->idle_timeout = g_timeout_add_seconds (3, - (GSourceFunc) idle_timeout, - sink); -} - -/** - * pinos_sink_report_busy: - * @sink: a #PinosSink - * - * Mark @sink as being busy. This will set the state of the sink - * to the RUNNING state. - */ -void -pinos_sink_report_busy (PinosSink *sink) -{ - g_return_if_fail (PINOS_IS_SINK (sink)); - - pinos_sink_set_state (sink, PINOS_SINK_STATE_RUNNING); -} - -/** - * pinos_sink_update_possible_formats: - * @sink: a #PinosSink - * @formats: a #GBytes - * - * Update the possible formats in @sink to @formats. This function also - * updates the possible formats of the channels. - */ -void -pinos_sink_update_possible_formats (PinosSink *sink, GBytes *formats) -{ - PinosSinkPrivate *priv; - GList *walk; - - g_return_if_fail (PINOS_IS_SINK (sink)); - priv = sink->priv; - - if (priv->iface) - g_object_set (priv->iface, "possible-formats", - g_bytes_get_data (formats, NULL), - NULL); - - for (walk = priv->channels; walk; walk = g_list_next (walk)) - g_object_set (walk->data, "possible-formats", formats, NULL); -} - -/** - * pinos_sink_update_format: - * @sink: a #PinosSink - * @format: a #GBytes - * - * Update the current format in @sink to @format. This function also - * updates the current format of the channels. - */ -void -pinos_sink_update_format (PinosSink *sink, GBytes *format) -{ - PinosSinkPrivate *priv; - GList *walk; - - g_return_if_fail (PINOS_IS_SINK (sink)); - priv = sink->priv; - - for (walk = priv->channels; walk; walk = g_list_next (walk)) - g_object_set (walk->data, "format", format, NULL); -} - -/** - * pinos_sink_create_channel: - * @sink: a #PinosSink - * @client_path: the client path - * @format_filter: a #GBytes - * @props: #PinosProperties - * @prefix: a prefix - * @error: a #GError or %NULL - * - * Create a new #PinosChannel for @sink. - * - * Returns: a new #PinosChannel or %NULL, in wich case @error will contain - * more information about the error. - */ -PinosChannel * -pinos_sink_create_channel (PinosSink *sink, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error) -{ - PinosSinkClass *klass; - PinosChannel *res; - - g_return_val_if_fail (PINOS_IS_SINK (sink), NULL); - - klass = PINOS_SINK_GET_CLASS (sink); - - if (klass->create_channel) { - res = klass->create_channel (sink, client_path, format_filter, props, prefix, error); - } else { - if (error) { - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "CreateChannel not implemented"); - } - res = NULL; - } - - return res; -} - -/** - * pinos_sink_release_channel: - * @sink: a #PinosSink - * @channel: a #PinosChannel - * - * Release the @channel in @sink. - * - * Returns: %TRUE on success. - */ -gboolean -pinos_sink_release_channel (PinosSink *sink, - PinosChannel *channel) -{ - PinosSinkClass *klass; - gboolean res; - - g_return_val_if_fail (PINOS_IS_SINK (sink), FALSE); - g_return_val_if_fail (PINOS_IS_CHANNEL (channel), FALSE); - - klass = PINOS_SINK_GET_CLASS (sink); - - if (klass->release_channel) - res = klass->release_channel (sink, channel); - else - res = FALSE; - - return res; -} diff --git a/pinos/server/sink.h b/pinos/server/sink.h deleted file mode 100644 index 0a231dd11..000000000 --- a/pinos/server/sink.h +++ /dev/null @@ -1,109 +0,0 @@ -/* Pinos - * Copyright (C) 2015 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __PINOS_SINK_H__ -#define __PINOS_SINK_H__ - -#include - -G_BEGIN_DECLS - -typedef struct _PinosSink PinosSink; -typedef struct _PinosSinkClass PinosSinkClass; -typedef struct _PinosSinkPrivate PinosSinkPrivate; - -#include -#include - -#define PINOS_TYPE_SINK (pinos_sink_get_type ()) -#define PINOS_IS_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SINK)) -#define PINOS_IS_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SINK)) -#define PINOS_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SINK, PinosSinkClass)) -#define PINOS_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SINK, PinosSink)) -#define PINOS_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SINK, PinosSinkClass)) -#define PINOS_SINK_CAST(obj) ((PinosSink*)(obj)) -#define PINOS_SINK_CLASS_CAST(klass) ((PinosSinkClass*)(klass)) - -/** - * PinosSink: - * - * Pinos sink object class. - */ -struct _PinosSink { - GObject object; - - PinosSinkPrivate *priv; -}; - -/** - * PinosSinkClass: - * @get_formats: called to get a list of supported formats from the sink - * @set_state: called to change the current state of the sink - * @create_channel: called to create a new channel object - * @release_channel: called to release a channel object - * - * Pinos sink object class. - */ -struct _PinosSinkClass { - GObjectClass parent_class; - - GBytes * (*get_formats) (PinosSink *sink, - GBytes *filter, - GError **error); - - gboolean (*set_state) (PinosSink *sink, PinosSinkState); - - PinosChannel * (*create_channel) (PinosSink *sink, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error); - gboolean (*release_channel) (PinosSink *sink, - PinosChannel *channel); -}; - -/* normal GObject stuff */ -GType pinos_sink_get_type (void); - -GBytes * pinos_sink_get_formats (PinosSink *sink, - GBytes *filter, - GError **error); - -gboolean pinos_sink_set_state (PinosSink *sink, PinosSinkState state); -void pinos_sink_update_state (PinosSink *sink, PinosSinkState state); -void pinos_sink_report_error (PinosSink *sink, GError *error); -void pinos_sink_report_idle (PinosSink *sink); -void pinos_sink_report_busy (PinosSink *sink); - -void pinos_sink_update_possible_formats (PinosSink *sink, GBytes *formats); -void pinos_sink_update_format (PinosSink *sink, GBytes *format); - -PinosChannel * pinos_sink_create_channel (PinosSink *sink, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error); -gboolean pinos_sink_release_channel (PinosSink *sink, - PinosChannel *channel); - -G_END_DECLS - -#endif /* __PINOS_SINK_H__ */ diff --git a/pinos/server/client-source.c b/pinos/server/upload-node.c similarity index 51% rename from pinos/server/client-source.c rename to pinos/server/upload-node.c index 857ea7cdb..7fd838b4d 100644 --- a/pinos/server/client-source.c +++ b/pinos/server/upload-node.c @@ -22,12 +22,12 @@ #include #include -#include +#include -#define PINOS_CLIENT_SOURCE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_CLIENT_SOURCE, PinosClientSourcePrivate)) +#define PINOS_UPLOAD_NODE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_UPLOAD_NODE, PinosUploadNodePrivate)) -struct _PinosClientSourcePrivate +struct _PinosUploadNodePrivate { GstElement *pipeline; GstElement *src; @@ -37,10 +37,12 @@ struct _PinosClientSourcePrivate GstCaps *format; GBytes *possible_formats; + PinosPort *input, *output; + PinosChannel *channel; }; -G_DEFINE_TYPE (PinosClientSource, pinos_client_source, PINOS_TYPE_SOURCE); +G_DEFINE_TYPE (PinosUploadNode, pinos_upload_node, PINOS_TYPE_NODE); enum { @@ -49,13 +51,13 @@ enum }; static void -client_source_get_property (GObject *_object, +upload_node_get_property (GObject *_object, guint prop_id, GValue *value, GParamSpec *pspec) { - PinosClientSource *source = PINOS_CLIENT_SOURCE (_object); - PinosClientSourcePrivate *priv = source->priv; + PinosUploadNode *node = PINOS_UPLOAD_NODE (_object); + PinosUploadNodePrivate *priv = node->priv; switch (prop_id) { case PROP_POSSIBLE_FORMATS: @@ -63,43 +65,47 @@ client_source_get_property (GObject *_object, break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (node, prop_id, pspec); break; } } static void -client_source_set_property (GObject *_object, +upload_node_set_property (GObject *_object, guint prop_id, const GValue *value, GParamSpec *pspec) { - PinosClientSource *source = PINOS_CLIENT_SOURCE (_object); - PinosClientSourcePrivate *priv = source->priv; + PinosUploadNode *node = PINOS_UPLOAD_NODE (_object); + PinosUploadNodePrivate *priv = node->priv; switch (prop_id) { case PROP_POSSIBLE_FORMATS: if (priv->possible_formats) g_bytes_unref (priv->possible_formats); priv->possible_formats = g_value_dup_boxed (value); - pinos_source_update_possible_formats (PINOS_SOURCE (source), - priv->possible_formats); + g_object_set (priv->output, "possible-formats", priv->possible_formats, NULL); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (node, prop_id, pspec); break; } } +static void +update_channel_format (PinosChannel *channel, GBytes *format) +{ + g_object_set (channel, "format", format, NULL); +} static gboolean bus_handler (GstBus *bus, GstMessage *message, gpointer user_data) { - PinosSource *source = user_data; - PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv; + PinosNode *node = user_data; + PinosUploadNodePrivate *priv = PINOS_UPLOAD_NODE (node)->priv; switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_ERROR: @@ -111,7 +117,7 @@ bus_handler (GstBus *bus, g_warning ("got error %s (%s)\n", error->message, debug); g_free (debug); - pinos_source_report_error (source, error); + pinos_node_report_error (node, error); gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; } @@ -128,9 +134,8 @@ bus_handler (GstBus *bus, caps_str = gst_caps_to_string (caps); format = g_bytes_new_take (caps_str, strlen (caps_str) + 1); - g_object_set (priv->channel, "possible-formats", format, "format", format, NULL); - pinos_source_update_possible_formats (source, format); - pinos_source_update_format (source, format); + g_list_foreach (pinos_port_get_channels (priv->output), (GFunc) update_channel_format, format); + g_list_foreach (pinos_port_get_channels (priv->input), (GFunc) update_channel_format, format); g_bytes_unref (format); } break; @@ -142,9 +147,9 @@ bus_handler (GstBus *bus, } static void -setup_pipeline (PinosClientSource *source) +setup_pipeline (PinosUploadNode *node) { - PinosClientSourcePrivate *priv = source->priv; + PinosUploadNodePrivate *priv = node->priv; GstBus *bus; priv->pipeline = gst_parse_launch ("socketsrc " @@ -159,116 +164,56 @@ setup_pipeline (PinosClientSource *source) priv->src = gst_bin_get_by_name (GST_BIN (priv->pipeline), "src"); bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline)); - priv->id = gst_bus_add_watch (bus, bus_handler, source); + priv->id = gst_bus_add_watch (bus, bus_handler, node); gst_object_unref (bus); - g_debug ("client-source %p: setup pipeline", source); -} - -static GstCaps * -collect_caps (PinosSource *source, - GstCaps *filter) -{ - PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv; - - if (priv->format) - return gst_caps_ref (priv->format); - else - return gst_caps_new_any (); + g_debug ("upload-node %p: setup pipeline", node); } static gboolean -client_set_state (PinosSource *source, - PinosSourceState state) +node_set_state (PinosNode *node, + PinosNodeState state) { - PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv; + PinosUploadNodePrivate *priv = PINOS_UPLOAD_NODE (node)->priv; switch (state) { - case PINOS_SOURCE_STATE_SUSPENDED: + case PINOS_NODE_STATE_SUSPENDED: gst_element_set_state (priv->pipeline, GST_STATE_NULL); break; - case PINOS_SOURCE_STATE_INITIALIZING: + case PINOS_NODE_STATE_INITIALIZING: gst_element_set_state (priv->pipeline, GST_STATE_READY); break; - case PINOS_SOURCE_STATE_IDLE: + case PINOS_NODE_STATE_IDLE: gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); break; - case PINOS_SOURCE_STATE_RUNNING: + case PINOS_NODE_STATE_RUNNING: gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); break; - case PINOS_SOURCE_STATE_ERROR: + case PINOS_NODE_STATE_ERROR: break; } - pinos_source_update_state (source, state); + pinos_node_update_state (node, state); return TRUE; } -static GBytes * -client_get_formats (PinosSource *source, - GBytes *filter, - GError **error) -{ - GstCaps *caps, *cfilter; - gchar *str; - - if (filter) { - cfilter = gst_caps_from_string (g_bytes_get_data (filter, NULL)); - if (cfilter == NULL) - goto invalid_filter; - } else { - cfilter = NULL; - } - - caps = collect_caps (source, cfilter); - if (caps == NULL) - goto no_format; - - str = gst_caps_to_string (caps); - - gst_caps_unref (caps); - if (cfilter) - gst_caps_unref (cfilter); - - return g_bytes_new_take (str, strlen (str) + 1); - -invalid_filter: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Invalid filter received"); - return NULL; - } -no_format: - { - if (error) - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "No compatible format found"); - if (cfilter) - gst_caps_unref (cfilter); - return NULL; - } -} - static void on_socket_notify (GObject *gobject, GParamSpec *pspec, gpointer user_data) { - PinosClientSource *source = user_data; - PinosClientSourcePrivate *priv = source->priv; + PinosUploadNode *node = user_data; + PinosUploadNodePrivate *priv = node->priv; GSocket *socket; guint num_handles; g_object_get (gobject, "socket", &socket, NULL); - g_debug ("client-source %p: output socket notify %p", source, socket); + g_debug ("upload-node %p: output socket notify %p", node, socket); if (socket == NULL) { GSocket *prev_socket = g_object_steal_data (gobject, "last-socket"); @@ -292,66 +237,39 @@ on_socket_notify (GObject *gobject, } } -static PinosChannel * -client_create_channel (PinosSource *source, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error) +static void +on_channel_added (PinosPort *port, PinosChannel *channel, PinosNode *node) { - PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (source)->priv; - PinosChannel *channel; + g_signal_connect (channel, "notify::socket", (GCallback) on_socket_notify, node); - /* propose format of input */ - g_object_get (priv->channel, "format", &format_filter, NULL); - - channel = PINOS_SOURCE_CLASS (pinos_client_source_parent_class) - ->create_channel (source, - client_path, - format_filter, - props, - prefix, - error); - g_bytes_unref (format_filter); - - if (channel == NULL) - return NULL; - - g_debug ("client-source %p: create channel %p", source, channel); - - g_signal_connect (channel, "notify::socket", (GCallback) on_socket_notify, source); - - return channel; -} - -static gboolean -client_release_channel (PinosSource *source, - PinosChannel *channel) -{ - g_debug ("client-source %p: release channel %p", source, channel); - return PINOS_SOURCE_CLASS (pinos_client_source_parent_class)->release_channel (source, channel); + g_debug ("upload-node %p: create channel %p", node, channel); } static void -client_source_dispose (GObject * object) +on_channel_removed (PinosPort *port, PinosChannel *channel, PinosNode *node) { - PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (object)->priv; + g_debug ("upload-node %p: release channel %p", node, channel); +} - g_debug ("client-source %p: dispose", object); +static void +upload_node_dispose (GObject * object) +{ + PinosUploadNodePrivate *priv = PINOS_UPLOAD_NODE (object)->priv; + + g_debug ("upload-node %p: dispose", object); g_source_remove (priv->id); gst_element_set_state (priv->pipeline, GST_STATE_NULL); - G_OBJECT_CLASS (pinos_client_source_parent_class)->dispose (object); + G_OBJECT_CLASS (pinos_upload_node_parent_class)->dispose (object); } static void -client_source_finalize (GObject * object) +upload_node_finalize (GObject * object) { - PinosClientSourcePrivate *priv = PINOS_CLIENT_SOURCE (object)->priv; + PinosUploadNodePrivate *priv = PINOS_UPLOAD_NODE (object)->priv; - g_debug ("client-source %p: finalize", object); + g_debug ("upload-node %p: finalize", object); g_clear_object (&priv->channel); g_clear_object (&priv->sink); @@ -362,7 +280,7 @@ client_source_finalize (GObject * object) g_bytes_unref (priv->possible_formats); gst_caps_replace (&priv->format, NULL); - G_OBJECT_CLASS (pinos_client_source_parent_class)->finalize (object); + G_OBJECT_CLASS (pinos_upload_node_parent_class)->finalize (object); } @@ -371,14 +289,14 @@ on_input_socket_notify (GObject *gobject, GParamSpec *pspec, gpointer user_data) { - PinosClientSource *source = user_data; - PinosClientSourcePrivate *priv = source->priv; + PinosUploadNode *node = user_data; + PinosUploadNodePrivate *priv = node->priv; GSocket *socket; GBytes *requested_format; GstCaps *caps; g_object_get (gobject, "socket", &socket, NULL); - g_debug ("client-source %p: input socket notify %p", source, socket); + g_debug ("upload-node %p: input socket notify %p", node, socket); if (socket) { /* requested format is final format */ @@ -397,11 +315,11 @@ on_input_socket_notify (GObject *gobject, g_object_set (priv->src, "socket", socket, NULL); if (socket) { - g_debug ("client-source %p: set pipeline to PLAYING", source); + g_debug ("upload-node %p: set pipeline to PLAYING", node); gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); g_object_unref (socket); } else { - g_debug ("client-source %p: set pipeline to READY", source); + g_debug ("upload-node %p: set pipeline to READY", node); gst_element_set_state (priv->pipeline, GST_STATE_READY); } } @@ -410,16 +328,16 @@ static void handle_remove_channel (PinosChannel *channel, gpointer user_data) { - PinosClientSource *source = user_data; - PinosClientSourcePrivate *priv = source->priv; + PinosUploadNode *node = user_data; + PinosUploadNodePrivate *priv = node->priv; - g_debug ("client-source %p: remove channel %p", source, priv->channel); + g_debug ("upload-node %p: remove channel %p", node, priv->channel); g_clear_pointer (&priv->channel, g_object_unref); } /** - * pinos_client_source_get_channel: - * @source: a #PinosClientSource + * pinos_upload_node_get_channel: + * @node: a #PinosUploadNode * @client_path: the client path * @format_filter: a #GBytes * @props: extra properties @@ -432,57 +350,54 @@ handle_remove_channel (PinosChannel *channel, * Returns: a new #PinosChannel. */ PinosChannel * -pinos_client_source_get_channel (PinosClientSource *source, - const gchar *client_path, - GBytes *format_filter, - PinosProperties *props, - const gchar *prefix, - GError **error) +pinos_upload_node_get_channel (PinosUploadNode *node, + const gchar *client_path, + GBytes *format_filter, + PinosProperties *props, + GError **error) { - PinosClientSourcePrivate *priv; + PinosUploadNodePrivate *priv; - g_return_val_if_fail (PINOS_IS_CLIENT_SOURCE (source), NULL); - priv = source->priv; + g_return_val_if_fail (PINOS_IS_UPLOAD_NODE (node), NULL); + priv = node->priv; if (priv->channel == NULL) { GstCaps *caps = gst_caps_from_string (g_bytes_get_data (format_filter, NULL)); gst_caps_take (&priv->format, caps); - priv->channel = PINOS_SOURCE_CLASS (pinos_client_source_parent_class) - ->create_channel (PINOS_SOURCE (source), - client_path, - format_filter, - props, - prefix, - error); + priv->channel = pinos_port_create_channel (priv->input, + client_path, + format_filter, + props, + error); if (priv->channel == NULL) return NULL; g_signal_connect (priv->channel, "remove", (GCallback) handle_remove_channel, - source); + node); - g_debug ("client-source %p: get source input %p", source, priv->channel); - g_signal_connect (priv->channel, "notify::socket", (GCallback) on_input_socket_notify, source); + g_debug ("upload-node %p: get input %p", node, priv->channel); + g_signal_connect (priv->channel, "notify::socket", (GCallback) on_input_socket_notify, node); } return g_object_ref (priv->channel); } static void -pinos_client_source_class_init (PinosClientSourceClass * klass) +pinos_upload_node_class_init (PinosUploadNodeClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - PinosSourceClass *source_class = PINOS_SOURCE_CLASS (klass); + PinosNodeClass *node_class = PINOS_NODE_CLASS (klass); - g_type_class_add_private (klass, sizeof (PinosClientSourcePrivate)); + g_type_class_add_private (klass, sizeof (PinosUploadNodePrivate)); - gobject_class->dispose = client_source_dispose; - gobject_class->finalize = client_source_finalize; + gobject_class->dispose = upload_node_dispose; + gobject_class->finalize = upload_node_finalize; - gobject_class->get_property = client_source_get_property; - gobject_class->set_property = client_source_set_property; + gobject_class->get_property = upload_node_get_property; + gobject_class->set_property = upload_node_set_property; g_object_class_install_property (gobject_class, PROP_POSSIBLE_FORMATS, @@ -494,37 +409,47 @@ pinos_client_source_class_init (PinosClientSourceClass * klass) G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - source_class->get_formats = client_get_formats; - source_class->set_state = client_set_state; - source_class->create_channel = client_create_channel; - source_class->release_channel = client_release_channel; + node_class->set_state = node_set_state; } static void -pinos_client_source_init (PinosClientSource * source) +pinos_upload_node_init (PinosUploadNode * node) { - source->priv = PINOS_CLIENT_SOURCE_GET_PRIVATE (source); + PinosUploadNodePrivate *priv = node->priv = PINOS_UPLOAD_NODE_GET_PRIVATE (node); - g_debug ("client-source %p: new", source); - setup_pipeline (source); + g_debug ("upload-node %p: new", node); + priv->input = pinos_port_new (PINOS_NODE (node), + PINOS_DIRECTION_INPUT, + "input", + priv->possible_formats, + NULL); + priv->output = pinos_port_new (PINOS_NODE (node), + PINOS_DIRECTION_OUTPUT, + "output", + priv->possible_formats, + NULL); + g_signal_connect (priv->output, "channel-added", (GCallback) on_channel_added, node); + g_signal_connect (priv->output, "channel-removed", (GCallback) on_channel_removed, node); + + setup_pipeline (node); } /** - * pinos_client_source_new: + * pinos_upload_node_new: * @daemon: the parent #PinosDaemon * @possible_formats: a #GBytes * - * Make a new #PinosSource that can be used to receive data from a client. + * Make a new #PinosNode that can be used to receive data from a client. * - * Returns: a new #PinosSource. + * Returns: a new #PinosNode. */ -PinosSource * -pinos_client_source_new (PinosDaemon *daemon, - GBytes *possible_formats) +PinosNode * +pinos_upload_node_new (PinosDaemon *daemon, + GBytes *possible_formats) { - return g_object_new (PINOS_TYPE_CLIENT_SOURCE, + return g_object_new (PINOS_TYPE_UPLOAD_NODE, "daemon", daemon, - "name", "client-source", + "name", "upload-node", "possible-formats", possible_formats, NULL); } diff --git a/pinos/server/upload-node.h b/pinos/server/upload-node.h new file mode 100644 index 000000000..16aefe0bf --- /dev/null +++ b/pinos/server/upload-node.h @@ -0,0 +1,75 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PINOS_UPLOAD_NODE_H__ +#define __PINOS_UPLOAD_NODE_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _PinosUploadNode PinosUploadNode; +typedef struct _PinosUploadNodeClass PinosUploadNodeClass; +typedef struct _PinosUploadNodePrivate PinosUploadNodePrivate; + +#include + +#define PINOS_TYPE_UPLOAD_NODE (pinos_upload_node_get_type ()) +#define PINOS_IS_UPLOAD_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_UPLOAD_NODE)) +#define PINOS_IS_UPLOAD_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_UPLOAD_NODE)) +#define PINOS_UPLOAD_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_UPLOAD_NODE, PinosUploadNodeClass)) +#define PINOS_UPLOAD_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_UPLOAD_NODE, PinosUploadNode)) +#define PINOS_UPLOAD_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_UPLOAD_NODE, PinosUploadNodeClass)) +#define PINOS_UPLOAD_NODE_CAST(obj) ((PinosUploadNode*)(obj)) +#define PINOS_UPLOAD_NODE_CLASS_CAST(klass) ((PinosUploadNodeClass*)(klass)) + +/** + * PinosUploadNode: + * + * Pinos client source object class. + */ +struct _PinosUploadNode { + PinosNode object; + + PinosUploadNodePrivate *priv; +}; + +/** + * PinosUploadNodeClass: + * + * Pinos client source object class. + */ +struct _PinosUploadNodeClass { + PinosNodeClass parent_class; +}; + +/* normal GObject stuff */ +GType pinos_upload_node_get_type (void); + +PinosNode * pinos_upload_node_new (PinosDaemon *daemon, + GBytes *possible_formats); + +PinosChannel * pinos_upload_node_get_channel (PinosUploadNode *source, + const gchar *client_path, + GBytes *format_filter, + PinosProperties *props, + GError **error); +G_END_DECLS + +#endif /* __PINOS_UPLOAD_NODE_H__ */ diff --git a/pinos/tests/test-client.c b/pinos/tests/test-client.c index e01299ca2..66fd9ec45 100644 --- a/pinos/tests/test-client.c +++ b/pinos/tests/test-client.c @@ -143,7 +143,7 @@ on_state_notify (GObject *gobject, g_signal_connect (stream, "notify::socket", (GCallback) on_socket_notify, stream); format = g_bytes_new_static (ANY_CAPS, strlen (ANY_CAPS) + 1); - pinos_stream_connect_source (stream, NULL, 0, format); + pinos_stream_connect (stream, PINOS_DIRECTION_OUTPUT, NULL, 0, format); g_bytes_unref (format); break; } diff --git a/pinos/tools/pinos-monitor.c b/pinos/tools/pinos-monitor.c index dadc26ac2..7abe8462e 100644 --- a/pinos/tools/pinos-monitor.c +++ b/pinos/tools/pinos-monitor.c @@ -137,32 +137,32 @@ dump_client_info (PinosContext *c, const PinosClientInfo *info, gpointer user_da } static void -dump_source_info (PinosContext *c, const PinosSourceInfo *info, gpointer user_data) +dump_node_info (PinosContext *c, const PinosNodeInfo *info, gpointer user_data) { DumpData *data = user_data; g_print ("\tid: %p\n", info->id); - g_print ("\tsource-path: \"%s\"\n", info->source_path); + g_print ("\tnode-path: \"%s\"\n", info->node_path); if (data->print_all) { g_print ("%c\tname: \"%s\"\n", MARK_CHANGE (0), info->name); print_properties (info->properties, MARK_CHANGE (1)); - g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (2), pinos_source_state_as_string (info->state)); - print_formats ("possible formats", info->possible_formats, MARK_CHANGE (3)); + g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (2), pinos_node_state_as_string (info->state)); } } static void -dump_sink_info (PinosContext *c, const PinosSinkInfo *info, gpointer user_data) +dump_port_info (PinosContext *c, const PinosPortInfo *info, gpointer user_data) { DumpData *data = user_data; g_print ("\tid: %p\n", info->id); - g_print ("\tsink-path: \"%s\"\n", info->sink_path); + g_print ("\tport-path: \"%s\"\n", info->port_path); if (data->print_all) { + g_print ("\tnode-path: \"%s\"\n", info->node_path); + g_print ("\tdirection: \"%s\"\n", pinos_direction_as_string (info->direction)); g_print ("%c\tname: \"%s\"\n", MARK_CHANGE (0), info->name); print_properties (info->properties, MARK_CHANGE (1)); - g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (2), pinos_sink_state_as_string (info->state)); - print_formats ("possible formats", info->possible_formats, MARK_CHANGE (3)); + print_formats ("possible formats", info->possible_formats, MARK_CHANGE (2)); } } @@ -175,12 +175,13 @@ dump_channel_info (PinosContext *c, const PinosChannelInfo *info, gpointer user_ g_print ("\tid: %p\n", info->id); g_print ("\tchannel-path: \"%s\"\n", info->channel_path); if (data->print_all) { - g_print ("%c\tclient-path: \"%s\"\n", MARK_CHANGE (0), info->client_path); - g_print ("%c\towner-path: \"%s\"\n", MARK_CHANGE (1), info->owner_path); - print_formats ("possible-formats", info->possible_formats, MARK_CHANGE (2)); - g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (3), pinos_channel_state_as_string (info->state)); + g_print ("\tdirection: \"%s\"\n", pinos_direction_as_string (info->direction)); + g_print ("\tclient-path: \"%s\"\n", info->client_path); + g_print ("%c\tport-path: \"%s\"\n", MARK_CHANGE (0), info->port_path); + print_properties (info->properties, MARK_CHANGE (1)); + g_print ("%c\tstate: \"%s\"\n", MARK_CHANGE (2), pinos_channel_state_as_string (info->state)); + print_formats ("possible-formats", info->possible_formats, MARK_CHANGE (3)); print_formats ("format", info->format, MARK_CHANGE (4)); - print_properties (info->properties, MARK_CHANGE (5)); } } @@ -205,20 +206,20 @@ dump_object (PinosContext *context, gpointer id, PinosSubscriptionFlags flags, info_ready, data); } - else if (flags & PINOS_SUBSCRIPTION_FLAG_SOURCE) { - pinos_context_get_source_info_by_id (context, - id, - PINOS_SOURCE_INFO_FLAGS_FORMATS, - dump_source_info, - NULL, - info_ready, - data); - } - else if (flags & PINOS_SUBSCRIPTION_FLAG_SINK) { - pinos_context_get_sink_info_by_id (context, + else if (flags & PINOS_SUBSCRIPTION_FLAG_NODE) { + pinos_context_get_node_info_by_id (context, id, - PINOS_SINK_INFO_FLAGS_FORMATS, - dump_sink_info, + PINOS_NODE_INFO_FLAGS_NONE, + dump_node_info, + NULL, + info_ready, + data); + } + else if (flags & PINOS_SUBSCRIPTION_FLAG_PORT) { + pinos_context_get_port_info_by_id (context, + id, + PINOS_PORT_INFO_FLAGS_FORMATS, + dump_port_info, NULL, info_ready, data);