diff --git a/pinos/Makefile.am b/pinos/Makefile.am index 1027e546b..9c15f652a 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -186,6 +186,7 @@ libpinos_@PINOS_MAJORMINOR@_la_SOURCES = \ client/properties.h client/properties.c \ client/stream.h client/stream.c \ client/pinos.c client/pinos.h \ + client/fdmanager.c client/fdmanager.h \ client/subscribe.c client/subscribe.h \ $(pinosgstsource) diff --git a/pinos/client/fdmanager.c b/pinos/client/fdmanager.c new file mode 100644 index 000000000..d984b956f --- /dev/null +++ b/pinos/client/fdmanager.c @@ -0,0 +1,328 @@ +/* GStreamer + * Copyright (C) 2016 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "fdmanager.h" + +struct _PinosFdManagerPrivate +{ + GMutex lock; + GHashTable *object_ids; + GHashTable *client_ids; + volatile gint id_counter; +}; + +#define PINOS_FD_MANAGER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_FD_MANAGER, PinosFdManagerPrivate)) + +G_DEFINE_TYPE (PinosFdManager, pinos_fd_manager, G_TYPE_OBJECT); + +static GMutex manager_lock; +static GHashTable *managers; + +typedef struct { + guint32 id; + gint refcount; + gpointer obj; + GDestroyNotify notify; +} ObjectId; + +static ObjectId * +object_id_new (guint32 id, gpointer obj, GDestroyNotify notify) +{ + ObjectId *oid; + + oid = g_slice_new (ObjectId); + oid->id = id; + oid->refcount = 1; + oid->obj = obj; + oid->notify = notify; + + return oid; +} + +static void +object_id_free (ObjectId *oid) +{ + g_assert (oid->refcount == 0); + oid->notify (oid->obj); + g_slice_free (ObjectId, oid); +} + +typedef struct { + GList *ids; +} ClientIds; + +static ClientIds * +client_ids_new (ObjectId *oid) +{ + ClientIds *ids; + + ids = g_slice_new (ClientIds); + ids->ids = g_list_prepend (NULL, oid); + + return ids; +} + +static void +client_ids_free (ClientIds *ids) +{ + g_list_free (ids->ids); + g_slice_free (ClientIds, ids); +} + +/** + * pinos_fd_manager_get: + * @type: the manager type + * + * Get a manager of @type. There will be a single instance of a #PinosFdManager + * per @type. + * + * Returns: a new reference to the #PinosFdManager for @type. + */ +PinosFdManager * +pinos_fd_manager_get (const gchar *type) +{ + PinosFdManager *manager; + + g_return_val_if_fail (type != NULL, NULL); + + g_type_class_ref (PINOS_TYPE_FD_MANAGER); + + g_mutex_lock (&manager_lock); + manager = g_hash_table_lookup (managers, type); + if (manager == NULL) { + manager = g_object_new (PINOS_TYPE_FD_MANAGER, NULL); + g_hash_table_insert (managers, g_strdup (type), manager); + } + g_mutex_unlock (&manager_lock); + + return g_object_ref (manager); +} + +/** + * pinos_fd_manager_get_id: + * @manager: a #PinosFdManager + * + * Get the next available id from @manager + * + * Returns: the next unused id. + */ +guint32 +pinos_fd_manager_get_id (PinosFdManager *manager) +{ + PinosFdManagerPrivate *priv; + + g_return_val_if_fail (PINOS_IS_FD_MANAGER (manager), -1); + + priv = manager->priv; + + return (guint32) g_atomic_int_add (&priv->id_counter, 1); +} + +/** + * pinos_fd_manager_add: + * @manager: a #PinosFdManager + * @client: a client id + * @id: an id + * @object: a pointer to an object to manage + * @notify: callback to free @object + * + * Associate @object with @id for @client. @object will be kept alive until a + * pinos_fd_manager_remove() or pinos_fd_manager_remove_client() with + * the same id and client is made. + * + * Returns: %TRUE on success. + */ +gboolean +pinos_fd_manager_add (PinosFdManager *manager, + const gchar *client, guint32 id, + gpointer object, GDestroyNotify notify) +{ + PinosFdManagerPrivate *priv; + ObjectId *oid; + ClientIds *cids; + + g_return_val_if_fail (PINOS_IS_FD_MANAGER (manager), FALSE); + g_return_val_if_fail (client != NULL, FALSE); + g_return_val_if_fail (object != NULL, FALSE); + + priv = manager->priv; + + g_mutex_lock (&priv->lock); + /* get the id */ + oid = g_hash_table_lookup (priv->object_ids, GINT_TO_POINTER (id)); + if (oid == NULL) { + /* first time, create and add */ + oid = object_id_new (id, object, notify); + g_hash_table_insert (priv->object_ids, GINT_TO_POINTER (id), oid); + } else { + /* existed, check if the same object and notify*/ + if (oid->obj != object || oid->notify != notify) + goto wrong_object; + /* increment refcount */ + oid->refcount++; + } + /* find the client and add the id */ + cids = g_hash_table_lookup (priv->client_ids, client); + if (cids == NULL) { + cids = client_ids_new (oid); + g_hash_table_insert (priv->client_ids, g_strdup (client), cids); + } else { + /* add the object to the client */ + cids->ids = g_list_prepend (cids->ids, oid); + } + g_mutex_unlock (&priv->lock); + + return TRUE; + + /* ERRORS */ +wrong_object: + { + g_warning ("wrong object"); + g_mutex_unlock (&priv->lock); + return FALSE; + } +} + +/** + * pinos_fd_manager_remove: + * @manager: a #PinosFdManager + * @client: a client id + * @id: an id + * + * Remove the id associated with client from @manager. + * + * Returns: %TRUE on success. + */ +gboolean +pinos_fd_manager_remove (PinosFdManager *manager, + const gchar *client, guint32 id) +{ + PinosFdManagerPrivate *priv; + ObjectId *oid; + ClientIds *cids; + + g_return_val_if_fail (PINOS_IS_FD_MANAGER (manager), FALSE); + g_return_val_if_fail (client != NULL, FALSE); + + priv = manager->priv; + + g_mutex_lock (&priv->lock); + oid = g_hash_table_lookup (priv->object_ids, GINT_TO_POINTER (id)); + if (oid) { + cids = g_hash_table_lookup (priv->client_ids, client); + if (cids) { + GList *find = g_list_find (cids->ids, oid); + + if (find) { + cids->ids = g_list_delete_link (cids->ids, find); + oid->refcount--; + if (oid->refcount == 0) { + g_hash_table_remove (priv->object_ids, GINT_TO_POINTER (id)); + } + } + } + } + g_mutex_unlock (&priv->lock); + + return TRUE; +} + +static void +remove_id (ObjectId *oid, PinosFdManager *manager) +{ + PinosFdManagerPrivate *priv = manager->priv; + + oid->refcount--; + if (oid->refcount == 0) { + g_hash_table_remove (priv->object_ids, GINT_TO_POINTER (oid->id)); + } +} + +/** + * pinos_fd_manager_remove_all: + * @manager: a #PinosFdManager + * @client: a client id + * + * Remove all ids from @manager associated with @client. + * + * Returns: %TRUE on success. + */ +gboolean +pinos_fd_manager_remove_all (PinosFdManager *manager, + const gchar *client) +{ + PinosFdManagerPrivate *priv; + ClientIds *cids; + + g_return_val_if_fail (PINOS_IS_FD_MANAGER (manager), FALSE); + g_return_val_if_fail (client != NULL, FALSE); + + priv = manager->priv; + + g_mutex_lock (&priv->lock); + cids = g_hash_table_lookup (priv->client_ids, client); + if (cids) { + g_list_foreach (cids->ids, (GFunc) remove_id, manager); + g_hash_table_remove (priv->client_ids, client); + client_ids_free (cids); + } + g_mutex_unlock (&priv->lock); + + return TRUE; +} + +static void +pinos_fd_manager_finalize (GObject * object) +{ + PinosFdManager *mgr = PINOS_FD_MANAGER (object); + PinosFdManagerPrivate *priv = mgr->priv; + + g_hash_table_unref (priv->object_ids); + g_hash_table_unref (priv->client_ids); + + G_OBJECT_CLASS (pinos_fd_manager_parent_class)->finalize (object); +} + +static void +pinos_fd_manager_init (PinosFdManager * mgr) +{ + PinosFdManagerPrivate *priv = mgr->priv = PINOS_FD_MANAGER_GET_PRIVATE (mgr); + + g_mutex_init (&priv->lock); + priv->object_ids = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) object_id_free); + priv->client_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); +} + +static void +pinos_fd_manager_class_init (PinosFdManagerClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = pinos_fd_manager_finalize; + + g_type_class_add_private (klass, sizeof (PinosFdManagerPrivate)); + + g_mutex_init (&manager_lock); + managers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +} diff --git a/pinos/client/fdmanager.h b/pinos/client/fdmanager.h new file mode 100644 index 000000000..25730701b --- /dev/null +++ b/pinos/client/fdmanager.h @@ -0,0 +1,76 @@ +/* GStreamer + * Copyright (C) 2016 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_FD_MANAGER_H__ +#define __PINOS_FD_MANAGER_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _PinosFdManager PinosFdManager; +typedef struct _PinosFdManagerClass PinosFdManagerClass; +typedef struct _PinosFdManagerPrivate PinosFdManagerPrivate; + +#define PINOS_TYPE_FD_MANAGER (pinos_fd_manager_get_type()) +#define PINOS_FD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),PINOS_TYPE_FD_MANAGER,PinosFdManager)) +#define PINOS_FD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),PINOS_TYPE_FD_MANAGER,PinosFdManagerClass)) +#define PINOS_IS_FD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),PINOS_TYPE_FD_MANAGER)) +#define PINOS_IS_FD_MANAGER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),PINOS_TYPE_FD_MANAGER)) + +/** + * PinosFdManager: + * + * Object to manager fds + */ +struct _PinosFdManager +{ + GObject parent; + + PinosFdManagerPrivate *priv; +}; + +struct _PinosFdManagerClass +{ + GObjectClass parent_class; +}; + +GType pinos_fd_manager_get_type (void); + +#define PINOS_FD_MANAGER_DEFAULT "default" + +PinosFdManager * pinos_fd_manager_get (const gchar *type); + +guint32 pinos_fd_manager_get_id (PinosFdManager *manager); + +gboolean pinos_fd_manager_add (PinosFdManager *manager, + const gchar *client, + guint32 id, + gpointer object, + GDestroyNotify notify); +gboolean pinos_fd_manager_remove (PinosFdManager *manager, + const gchar *client, + guint32 id); +gboolean pinos_fd_manager_remove_all (PinosFdManager *manager, + const gchar *client); + + +G_END_DECLS + +#endif /* __PINOS_FD_MANAGER_H__ */ diff --git a/pinos/client/pinos.h b/pinos/client/pinos.h index ffc1d5076..316b385de 100644 --- a/pinos/client/pinos.h +++ b/pinos/client/pinos.h @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include #include diff --git a/pinos/gst/gstpinospay.c b/pinos/gst/gstpinospay.c index 7a5b63c04..46ee3b296 100644 --- a/pinos/gst/gstpinospay.c +++ b/pinos/gst/gstpinospay.c @@ -52,8 +52,6 @@ #include #include -#include - GST_DEBUG_CATEGORY_STATIC (gst_pinos_pay_debug_category); #define GST_CAT_DEFAULT gst_pinos_pay_debug_category @@ -166,43 +164,31 @@ gst_pinos_pay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) return res; } -static void -client_object_destroy (GHashTable *hash) -{ - GST_LOG ("%p: hash destroyed", hash); - g_hash_table_unref (hash); -} - static void client_buffer_sent (GstPinosPay *pay, GstBuffer *buffer, GObject *obj) { GArray *fdids; - GHashTable *hash; guint i; + const gchar *client_path; fdids = gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), fdids_quark); if (fdids == NULL) return; - /* we keep a hashtable of fd ids on the sender object (usually GSocket) itself, - * when the object is destroyed, we automatically also release the refcounts */ - hash = g_object_get_qdata (obj, fdids_quark); - if (hash == NULL) { - hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, - (GDestroyNotify) gst_buffer_unref); - g_object_set_qdata_full (obj, fdids_quark, hash, - (GDestroyNotify) client_object_destroy); - GST_LOG ("%p: new hash for object %p", hash, obj); - } + /* get the client path of this socket */ + client_path = g_object_get_data (obj, "pinos-client-path"); + if (client_path == NULL) + return; for (i = 0; i < fdids->len; i++) { gint id = g_array_index (fdids, guint32, i); - gboolean res; - - GST_LOG ("%p: fd index %d, increment refcount of buffer %p", hash, id, buffer); - res = g_hash_table_insert (hash, GINT_TO_POINTER (id), gst_buffer_ref (buffer)); -// g_assert (res); + /* now store the id/client-path/buffer in the fdmanager */ + GST_LOG ("fd index %d, client %s increment refcount of buffer %p", id, client_path, buffer); + pinos_fd_manager_add (pay->fdmanager, + client_path, id, + gst_buffer_ref (buffer), + (GDestroyNotify) gst_buffer_unref); } } @@ -213,10 +199,10 @@ client_buffer_received (GstPinosPay *pay, GstBuffer *buffer, PinosBuffer pbuf; PinosBufferIter it; GstMapInfo info; - GHashTable *hash; + const gchar *client_path; - hash = g_object_get_qdata (obj, fdids_quark); - if (hash == NULL) + client_path = g_object_get_data (obj, "pinos-client-path"); + if (client_path == NULL) return; gst_buffer_map (buffer, &info, GST_MAP_READ); @@ -228,16 +214,14 @@ client_buffer_received (GstPinosPay *pay, GstBuffer *buffer, { PinosPacketReleaseFDPayload p; gint id; - gboolean res; if (!pinos_buffer_iter_parse_release_fd_payload (&it, &p)) continue; id = p.id; - GST_LOG ("%p: fd index %d is released", hash, id); - res = g_hash_table_remove (hash, GINT_TO_POINTER (id)); - //g_assert (res); + GST_LOG ("fd index %d for client %s is released", id, client_path); + pinos_fd_manager_remove (pay->fdmanager, client_path, id); break; } default: @@ -427,7 +411,7 @@ gst_pinos_pay_chain_other (GstPinosPay *pay, GstBuffer * buffer) p.fd_index = pinos_buffer_builder_add_fd (&builder, gst_fd_memory_get_fd (fdmem), &err); if (p.fd_index == -1) goto add_fd_failed; - p.id = pay->id_counter++; + p.id = pinos_fd_manager_get_id (pay->fdmanager); p.offset = fdmem->offset; p.size = fdmem->size; pinos_buffer_builder_add_fd_payload (&builder, &p); @@ -497,6 +481,7 @@ gst_pinos_pay_finalize (GObject * object) GST_DEBUG_OBJECT (pay, "finalize"); g_object_unref (pay->allocator); + g_object_unref (pay->fdmanager); G_OBJECT_CLASS (gst_pinos_pay_parent_class)->finalize (object); } @@ -515,6 +500,7 @@ gst_pinos_pay_init (GstPinosPay * pay) gst_element_add_pad (GST_ELEMENT (pay), pay->sinkpad); pay->allocator = gst_tmpfile_allocator_new (); + pay->fdmanager = pinos_fd_manager_get (PINOS_FD_MANAGER_DEFAULT); } static void diff --git a/pinos/gst/gstpinospay.h b/pinos/gst/gstpinospay.h index 26be3b87b..713211952 100644 --- a/pinos/gst/gstpinospay.h +++ b/pinos/gst/gstpinospay.h @@ -23,6 +23,8 @@ #include +#include + G_BEGIN_DECLS #define GST_TYPE_PINOS_PAY (gst_pinos_pay_get_type()) @@ -43,7 +45,7 @@ struct _GstPinosPay GstAllocator *allocator; - guint32 id_counter; + PinosFdManager *fdmanager; }; struct _GstPinosPayClass diff --git a/pinos/server/client.c b/pinos/server/client.c index f62b22873..6fb39e37f 100644 --- a/pinos/server/client.c +++ b/pinos/server/client.c @@ -20,8 +20,6 @@ #include #include "pinos/client/pinos.h" -#include "pinos/client/enumtypes.h" - #include "pinos/server/client.h" #include "pinos/server/client-source.h" @@ -36,6 +34,7 @@ struct _PinosClientPrivate PinosClient1 *client1; + PinosFdManager *fdmanager; GList *outputs; }; @@ -371,6 +370,9 @@ pinos_client_dispose (GObject * object) PinosClient *client = PINOS_CLIENT (object); PinosClientPrivate *priv = client->priv; + if (priv->object_path) + pinos_fd_manager_remove_all (priv->fdmanager, priv->object_path); + g_list_foreach (priv->outputs, (GFunc) do_remove_output, client); client_unregister_object (client); g_clear_object (&priv->daemon); @@ -386,6 +388,7 @@ pinos_client_finalize (GObject * object) g_free (priv->sender); if (priv->properties) pinos_properties_free (priv->properties); + g_clear_object (&priv->fdmanager); G_OBJECT_CLASS (pinos_client_parent_class)->finalize (object); } @@ -469,7 +472,9 @@ pinos_client_class_init (PinosClientClass * klass) static void pinos_client_init (PinosClient * client) { - client->priv = PINOS_CLIENT_GET_PRIVATE (client); + PinosClientPrivate *priv = client->priv = PINOS_CLIENT_GET_PRIVATE (client); + + priv->fdmanager = pinos_fd_manager_get (PINOS_FD_MANAGER_DEFAULT); } diff --git a/pinos/server/source-output.c b/pinos/server/source-output.c index aa137d02b..aa9a5ed7b 100644 --- a/pinos/server/source-output.c +++ b/pinos/server/source-output.c @@ -232,6 +232,7 @@ handle_start (PinosSourceOutput1 *interface, g_clear_object (&priv->socket); priv->socket = g_socket_new_from_fd (fd[0], NULL); + g_object_set_data (priv->socket, "pinos-client-path", priv->client_path); g_object_notify (G_OBJECT (output), "socket"); /* the notify of the socket above should configure the format */