From b30202b17450f5f591328255c712b1b19c90e779 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 14 Nov 2012 15:49:06 +0100 Subject: [PATCH] address-pool: add object to manage multicast addresses Make an object that can manage a rage of multicast addresses and ports. --- gst/rtsp-server/Makefile.am | 2 + gst/rtsp-server/rtsp-address-pool.c | 428 ++++++++++++++++++++++++++++ gst/rtsp-server/rtsp-address-pool.h | 85 ++++++ 3 files changed, 515 insertions(+) create mode 100644 gst/rtsp-server/rtsp-address-pool.c create mode 100644 gst/rtsp-server/rtsp-address-pool.h diff --git a/gst/rtsp-server/Makefile.am b/gst/rtsp-server/Makefile.am index 2d6a603875..f8da7447cd 100644 --- a/gst/rtsp-server/Makefile.am +++ b/gst/rtsp-server/Makefile.am @@ -1,5 +1,6 @@ public_headers = \ rtsp-auth.h \ + rtsp-address-pool.h \ rtsp-params.h \ rtsp-sdp.h \ rtsp-media.h \ @@ -16,6 +17,7 @@ public_headers = \ c_sources = \ rtsp-auth.c \ + rtsp-address-pool.c \ rtsp-params.c \ rtsp-sdp.c \ rtsp-media.c \ diff --git a/gst/rtsp-server/rtsp-address-pool.c b/gst/rtsp-server/rtsp-address-pool.c new file mode 100644 index 0000000000..8a9e4a7f89 --- /dev/null +++ b/gst/rtsp-server/rtsp-address-pool.c @@ -0,0 +1,428 @@ +/* GStreamer + * Copyright (C) 2012 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 "rtsp-address-pool.h" + +GST_DEBUG_CATEGORY_STATIC (rtsp_address_pool_debug); +#define GST_CAT_DEFAULT rtsp_address_pool_debug + +#define GST_RTSP_ADDRESS_POOL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolPrivate)) + +struct _GstRTSPAddressPoolPrivate +{ + GMutex lock; + GList *addresses; + GList *allocated; +}; + +#define ADDR_IS_IPV4(a) ((a)->size == 4) +#define ADDR_IS_IPV6(a) ((a)->size == 16) +#define ADDR_IS_EVEN_PORT(a) (((a)->port & 1) == 0) + +typedef struct +{ + guint8 bytes[16]; + gsize size; + guint16 port; +} Addr; + +typedef struct +{ + Addr min; + Addr max; + guint8 ttl; +} AddrRange; + +#define RANGE_IS_SINGLE(r) (memcmp ((r)->min.bytes, (r)->max.bytes, (r)->min.size) == 0) + +#define gst_rtsp_address_pool_parent_class parent_class +G_DEFINE_TYPE (GstRTSPAddressPool, gst_rtsp_address_pool, G_TYPE_OBJECT); + +static void gst_rtsp_address_pool_finalize (GObject * obj); + +static void +gst_rtsp_address_pool_class_init (GstRTSPAddressPoolClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_rtsp_address_pool_finalize; + + g_type_class_add_private (klass, sizeof (GstRTSPAddressPoolPrivate)); + + GST_DEBUG_CATEGORY_INIT (rtsp_address_pool_debug, "rtspaddresspool", 0, + "GstRTSPAddressPool"); +} + +static void +gst_rtsp_address_pool_init (GstRTSPAddressPool * pool) +{ + pool->priv = GST_RTSP_ADDRESS_POOL_GET_PRIVATE (pool); + + g_mutex_init (&pool->priv->lock); +} + +static void +free_range (AddrRange * range) +{ + g_slice_free (AddrRange, range); +} + +static void +gst_rtsp_address_pool_finalize (GObject * obj) +{ + GstRTSPAddressPool *pool; + + pool = GST_RTSP_ADDRESS_POOL (obj); + + g_list_free_full (pool->priv->addresses, (GDestroyNotify) free_range); + g_list_free_full (pool->priv->allocated, (GDestroyNotify) free_range); + g_mutex_clear (&pool->priv->lock); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +/** + * gst_rtsp_address_pool_new: + * + * Make a new #GstRTSPAddressPool. + * + * Returns: a new #GstRTSPAddressPool + */ +GstRTSPAddressPool * +gst_rtsp_address_pool_new (void) +{ + GstRTSPAddressPool *pool; + + pool = g_object_new (GST_TYPE_RTSP_ADDRESS_POOL, NULL); + + return pool; +} + +static gboolean +fill_address (const gchar * address, guint16 port, Addr * addr) +{ + GInetAddress *inet; + + inet = g_inet_address_new_from_string (address); + if (inet == NULL) + return FALSE; + + addr->size = g_inet_address_get_native_size (inet); + memcpy (addr->bytes, g_inet_address_to_bytes (inet), addr->size); + g_object_unref (inet); + addr->port = port; + + return TRUE; +} + +/** + * gst_rtsp_address_pool_add_range: + * @pool: a #GstRTSPAddressPool + * @min_address: a minimum address to add + * @max_address: a maximum address to add + * @min_port: the minimum port + * @max_port: the maximum port + * @ttl: a TTL + * + * Adds the multicast addresses from @min_addess to @max_address (inclusive) + * to @pool. The valid port range for the addresses will be from @min_port to + * @max_port inclusive. + * + * Returns: %TRUE if the addresses could be added. + */ +gboolean +gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool, + const gchar * min_address, const gchar * max_address, + guint16 min_port, guint16 max_port, guint8 ttl) +{ + AddrRange *range; + GstRTSPAddressPoolPrivate *priv; + + g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE); + g_return_val_if_fail (min_port <= max_port, FALSE); + + priv = pool->priv; + + range = g_slice_new0 (AddrRange); + + if (!fill_address (min_address, min_port, &range->min)) + goto invalid; + if (!fill_address (max_address, max_port, &range->max)) + goto invalid; + + if (range->min.size != range->max.size) + goto invalid; + if (memcmp (range->min.bytes, range->max.bytes, range->min.size) > 0) + goto invalid; + + range->ttl = ttl; + + GST_DEBUG_OBJECT (pool, "adding %s-%s:%u-%u ttl %u", min_address, max_address, + min_port, max_port, ttl); + + g_mutex_lock (&priv->lock); + priv->addresses = g_list_prepend (priv->addresses, range); + g_mutex_unlock (&priv->lock); + + return TRUE; + + /* ERRORS */ +invalid: + { + GST_ERROR_OBJECT (pool, "invalid address range %s-%s", min_address, + max_address); + g_slice_free (AddrRange, range); + return FALSE; + } +} + +static void +inc_address (GstRTSPAddressPool * pool, Addr * addr) +{ + gint i; + guint carry; + + carry = 1; + for (i = addr->size - 1; i >= 0 && carry > 0; i--) { + carry += addr->bytes[i]; + addr->bytes[i] = carry & 0xff; + carry >>= 8; + } +} + +static AddrRange * +split_range (GstRTSPAddressPool * pool, AddrRange * range, gint skip, + gint n_ports) +{ + GstRTSPAddressPoolPrivate *priv = pool->priv; + AddrRange *temp; + + if (!RANGE_IS_SINGLE (range)) { + /* min and max are not the same, we have more than one address. */ + temp = g_slice_dup (AddrRange, range); + /* increment the range min address */ + inc_address (pool, &temp->min); + /* and store back in pool */ + priv->addresses = g_list_prepend (priv->addresses, temp); + + /* adjust range with only the first address */ + memcpy (range->max.bytes, range->min.bytes, range->min.size); + } + + /* range now contains only one single address */ + if (skip > 0) { + /* make a range with the skipped ports */ + temp = g_slice_dup (AddrRange, range); + temp->max.port = temp->min.port + skip; + /* and store back in pool */ + priv->addresses = g_list_prepend (priv->addresses, temp); + + /* increment range port */ + range->min.port += skip; + } + /* range now contains single address with desired port number */ + if (range->max.port - range->min.port + 1 > n_ports) { + /* make a range with the remaining ports */ + temp = g_slice_dup (AddrRange, range); + temp->min.port += n_ports; + /* and store back in pool */ + priv->addresses = g_list_prepend (priv->addresses, temp); + + /* and truncate port */ + range->max.port = range->min.port + n_ports - 1; + } + return range; +} + +/** + * gst_rtsp_address_pool_acquire_address: + * @pool: a #GstRTSPAddressPool + * @flags: flags + * @n_ports: the amount of ports + * @address: result address + * @port: result port + * @ttl: result TTL + * + * Take an address and ports from @pool. @flags can be used to control the + * allocation. @n_ports consecutive ports will be allocated of which the first + * one can be found in @port. + * + * Returns: a pointer that should be used to release the address with + * gst_rtsp_address_pool_release_address() after usage or %NULL when no + * address could be acquired. + */ +gpointer +gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool, + GstRTSPAddressFlags flags, gint n_ports, gchar ** address, + guint16 * port, guint8 * ttl) +{ + GstRTSPAddressPoolPrivate *priv; + GList *walk, *next; + AddrRange *result; + + g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL); + g_return_val_if_fail (n_ports > 0, NULL); + g_return_val_if_fail (address != NULL, NULL); + g_return_val_if_fail (port != NULL, NULL); + g_return_val_if_fail (ttl != NULL, NULL); + + priv = pool->priv; + result = NULL; + + g_mutex_lock (&priv->lock); + /* go over available ranges */ + for (walk = priv->addresses; walk; walk = next) { + AddrRange *range; + gint ports, skip; + + range = walk->data; + next = walk->next; + + /* check address type when given */ + if (flags & GST_RTSP_ADDRESS_FLAG_IPV4 && !ADDR_IS_IPV4 (&range->min)) + continue; + if (flags & GST_RTSP_ADDRESS_FLAG_IPV6 && !ADDR_IS_IPV6 (&range->min)) + continue; + + /* check for enough ports */ + ports = range->max.port - range->min.port + 1; + if (flags & GST_RTSP_ADDRESS_FLAG_EVEN_PORT + && !ADDR_IS_EVEN_PORT (&range->min)) + skip = 1; + else + skip = 0; + if (ports - skip < n_ports) + continue; + + /* we found a range, remove from the list */ + priv->addresses = g_list_delete_link (priv->addresses, walk); + /* now split and exit our loop */ + result = split_range (pool, range, skip, n_ports); + priv->allocated = g_list_prepend (priv->allocated, result); + break; + } + g_mutex_unlock (&priv->lock); + + if (result) { + GInetAddress *inet; + + inet = g_inet_address_new_from_bytes (result->min.bytes, + result->min.size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6); + + *address = g_inet_address_to_string (inet); + g_object_unref (inet); + + *port = result->min.port; + *ttl = result->ttl; + + GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", *address, *port, *ttl); + } + return result; +} + +/** + * gst_rtsp_address_pool_release_address: + * @pool: a #GstRTSPAddressPool + * @id: an address id + * + * Release a previously acquired address (with + * gst_rtsp_address_pool_acquire_address()) back into @pool. + */ +void +gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool, gpointer id) +{ + GstRTSPAddressPoolPrivate *priv; + GList *find; + + g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool)); + g_return_if_fail (id != NULL); + + priv = pool->priv; + + g_mutex_lock (&priv->lock); + find = g_list_find (priv->allocated, id); + if (find == NULL) + goto not_found; + + priv->allocated = g_list_delete_link (priv->allocated, find); + + /* FIXME, merge and do something clever */ + priv->addresses = g_list_prepend (priv->addresses, id); + g_mutex_unlock (&priv->lock); + + return; + + /* ERRORS */ +not_found: + { + g_mutex_unlock (&priv->lock); + return; + } +} + +static void +dump_range (GstRTSPAddressPool * pool, AddrRange * range) +{ + GInetAddress *inet; + gchar *addr1, *addr2; + + inet = g_inet_address_new_from_bytes (range->min.bytes, + range->max.size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6); + addr1 = g_inet_address_to_string (inet); + g_object_unref (inet); + + inet = g_inet_address_new_from_bytes (range->max.bytes, + range->max.size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6); + addr2 = g_inet_address_to_string (inet); + g_object_unref (inet); + + g_print (" address %s-%s, port %u-%u, ttl %u\n", addr1, addr2, + range->min.port, range->max.port, range->ttl); + + g_free (addr1); + g_free (addr2); +} + +void +gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool) +{ + GstRTSPAddressPoolPrivate *priv; + GList *walk; + + g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool)); + + priv = pool->priv; + + g_mutex_lock (&priv->lock); + g_print ("free:\n"); + for (walk = priv->addresses; walk; walk = walk->next) { + dump_range (pool, (AddrRange *) walk->data); + } + g_print ("allocated:\n"); + for (walk = priv->allocated; walk; walk = walk->next) { + dump_range (pool, (AddrRange *) walk->data); + } + g_mutex_unlock (&priv->lock); +} diff --git a/gst/rtsp-server/rtsp-address-pool.h b/gst/rtsp-server/rtsp-address-pool.h new file mode 100644 index 0000000000..57a52fc401 --- /dev/null +++ b/gst/rtsp-server/rtsp-address-pool.h @@ -0,0 +1,85 @@ +/* GStreamer + * Copyright (C) 2012 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 + +#ifndef __GST_RTSP_ADDRESS_POOL_H__ +#define __GST_RTSP_ADDRESS_POOL_H__ + +G_BEGIN_DECLS + +#define GST_TYPE_RTSP_ADDRESS_POOL (gst_rtsp_address_pool_get_type ()) +#define GST_IS_RTSP_ADDRESS_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ADDRESS_POOL)) +#define GST_IS_RTSP_ADDRESS_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ADDRESS_POOL)) +#define GST_RTSP_ADDRESS_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolClass)) +#define GST_RTSP_ADDRESS_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPool)) +#define GST_RTSP_ADDRESS_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolClass)) +#define GST_RTSP_ADDRESS_POOL_CAST(obj) ((GstRTSPAddressPool*)(obj)) +#define GST_RTSP_ADDRESS_POOL_CLASS_CAST(klass) ((GstRTSPAddressPoolClass*)(klass)) + +typedef struct _GstRTSPAddressPool GstRTSPAddressPool; +typedef struct _GstRTSPAddressPoolClass GstRTSPAddressPoolClass; +typedef struct _GstRTSPAddressPoolPrivate GstRTSPAddressPoolPrivate; + +typedef enum { + GST_RTSP_ADDRESS_FLAG_NONE = 0, + GST_RTSP_ADDRESS_FLAG_IPV4 = (1 << 0), + GST_RTSP_ADDRESS_FLAG_IPV6 = (1 << 1), + GST_RTSP_ADDRESS_FLAG_EVEN_PORT = (1 << 2) +} GstRTSPAddressFlags; + +/** + * GstRTSPAddressPool: + * @parent: the parent GObject + * + */ +struct _GstRTSPAddressPool { + GObject parent; + + GstRTSPAddressPoolPrivate *priv; +}; + +struct _GstRTSPAddressPoolClass { + GObjectClass parent_class; +}; + +GType gst_rtsp_address_pool_get_type (void); + +/* create a new address pool */ +GstRTSPAddressPool * gst_rtsp_address_pool_new (void); + +void gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool); + +gboolean gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool, + const gchar *min_address, + const gchar *max_address, + guint16 min_port, + guint16 max_port, + guint8 ttl); + +gpointer gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool, + GstRTSPAddressFlags flags, + gint n_ports, + gchar **address, + guint16 *port, guint8 *ttl); +void gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool, + gpointer); +G_END_DECLS + +#endif /* __GST_RTSP_ADDRESS_POOL_H__ */