mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
7b0ad7c25f
Let gst_rtsp_address_pool_reserve_address() return the reason why the address could not be reserved. Fixes https://bugzilla.gnome.org/show_bug.cgi?id=708229
750 lines
20 KiB
C
750 lines
20 KiB
C
/* GStreamer
|
|
* Copyright (C) 2012 Wim Taymans <wim.taymans at gmail.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
/**
|
|
* SECTION:rtsp-address-pool
|
|
* @short_description: A pool of network addresses
|
|
* @see_also: #GstRTSPStream, #GstRTSPStreamTransport
|
|
*
|
|
* The #GstRTSPAddressPool is an object that maintains a collection of network
|
|
* addresses. It is used to allocate server ports and server multicast addresses
|
|
* but also to reserve client provided destination addresses.
|
|
*
|
|
* A range of addresses can be added with gst_rtsp_address_pool_add_range().
|
|
* Both multicast and unicast addresses can be added.
|
|
*
|
|
* With gst_rtsp_address_pool_acquire_address() an unused address and port range
|
|
* can be acquired from the pool. With gst_rtsp_address_pool_reserve_address() a
|
|
* specific address can be retrieved. Both methods return a boxed
|
|
* #GstRTSPAddress that should be freed with gst_rtsp_address_free() after
|
|
* usage, which brings the address back into the pool.
|
|
*
|
|
* Last reviewed on 2013-07-16 (1.0.0)
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <gio/gio.h>
|
|
|
|
#include "rtsp-address-pool.h"
|
|
|
|
/**
|
|
* gst_rtsp_address_copy:
|
|
* @addr: a #GstRTSPAddress
|
|
*
|
|
* Make a copy of @addr.
|
|
*
|
|
* Returns: a copy of @addr.
|
|
*/
|
|
GstRTSPAddress *
|
|
gst_rtsp_address_copy (GstRTSPAddress * addr)
|
|
{
|
|
GstRTSPAddress *copy;
|
|
|
|
g_return_val_if_fail (addr != NULL, NULL);
|
|
|
|
copy = g_slice_dup (GstRTSPAddress, addr);
|
|
/* only release to the pool when the original is freed. It's a bit
|
|
* weird but this will do for now as it avoid us to use refcounting. */
|
|
copy->pool = NULL;
|
|
copy->address = g_strdup (copy->address);
|
|
|
|
return copy;
|
|
}
|
|
|
|
static void gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
|
|
GstRTSPAddress * addr);
|
|
|
|
/**
|
|
* gst_rtsp_address_free:
|
|
* @addr: a #GstRTSPAddress
|
|
*
|
|
* Free @addr and releasing it back into the pool when owned by a
|
|
* pool.
|
|
*/
|
|
void
|
|
gst_rtsp_address_free (GstRTSPAddress * addr)
|
|
{
|
|
g_return_if_fail (addr != NULL);
|
|
|
|
if (addr->pool) {
|
|
/* unrefs the pool and sets it to NULL */
|
|
gst_rtsp_address_pool_release_address (addr->pool, addr);
|
|
}
|
|
g_free (addr->address);
|
|
g_slice_free (GstRTSPAddress, addr);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstRTSPAddress, gst_rtsp_address,
|
|
(GBoxedCopyFunc) gst_rtsp_address_copy,
|
|
(GBoxedFreeFunc) gst_rtsp_address_free);
|
|
|
|
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; /* protects everything in this struct */
|
|
GList *addresses;
|
|
GList *allocated;
|
|
|
|
gboolean has_unicast_addresses;
|
|
};
|
|
|
|
#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;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_address_pool_clear:
|
|
* @pool: a #GstRTSPAddressPool
|
|
*
|
|
* Clear all addresses in @pool. There should be no outstanding
|
|
* allocations.
|
|
*/
|
|
void
|
|
gst_rtsp_address_pool_clear (GstRTSPAddressPool * pool)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
|
|
g_return_if_fail (pool->priv->allocated == NULL);
|
|
|
|
priv = pool->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
g_list_free_full (priv->addresses, (GDestroyNotify) free_range);
|
|
priv->addresses = NULL;
|
|
g_mutex_unlock (&priv->lock);
|
|
}
|
|
|
|
static gboolean
|
|
fill_address (const gchar * address, guint16 port, Addr * addr,
|
|
gboolean is_multicast)
|
|
{
|
|
GInetAddress *inet;
|
|
|
|
inet = g_inet_address_new_from_string (address);
|
|
if (inet == NULL)
|
|
return FALSE;
|
|
|
|
if (is_multicast != g_inet_address_get_is_multicast (inet)) {
|
|
g_object_unref (inet);
|
|
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;
|
|
}
|
|
|
|
static gchar *
|
|
get_address_string (Addr * addr)
|
|
{
|
|
gchar *res;
|
|
GInetAddress *inet;
|
|
|
|
inet = g_inet_address_new_from_bytes (addr->bytes,
|
|
addr->size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
|
|
res = g_inet_address_to_string (inet);
|
|
g_object_unref (inet);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* 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 or 0 for unicast addresses
|
|
*
|
|
* Adds the 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.
|
|
*
|
|
* When @ttl is 0, @min_address and @max_address should be unicast addresses.
|
|
* @min_address and @max_address can be set to
|
|
* #GST_RTSP_ADDRESS_POOL_ANY_IPV4 or #GST_RTSP_ADDRESS_POOL_ANY_IPV6 to bind
|
|
* to all available IPv4 or IPv6 addresses.
|
|
*
|
|
* When @ttl > 0, @min_address and @max_address should be multicast addresses.
|
|
*
|
|
* 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;
|
|
gboolean is_multicast;
|
|
|
|
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;
|
|
|
|
is_multicast = ttl != 0;
|
|
|
|
range = g_slice_new0 (AddrRange);
|
|
|
|
if (!fill_address (min_address, min_port, &range->min, is_multicast))
|
|
goto invalid;
|
|
if (!fill_address (max_address, max_port, &range->max, is_multicast))
|
|
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);
|
|
|
|
if (!is_multicast)
|
|
priv->has_unicast_addresses = TRUE;
|
|
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 (Addr * addr, guint8 count)
|
|
{
|
|
gint i;
|
|
guint carry;
|
|
|
|
carry = count;
|
|
for (i = addr->size - 1; i >= 0 && carry > 0; i--) {
|
|
carry += addr->bytes[i];
|
|
addr->bytes[i] = carry & 0xff;
|
|
carry >>= 8;
|
|
}
|
|
}
|
|
|
|
/* tells us the number of addresses between min_addr and max_addr */
|
|
|
|
static guint
|
|
diff_address (Addr * max_addr, Addr * min_addr)
|
|
{
|
|
gint i;
|
|
guint result = 0;
|
|
|
|
g_return_val_if_fail (min_addr->size == max_addr->size, 0);
|
|
|
|
for (i = 0; i < min_addr->size; i++) {
|
|
g_return_val_if_fail (result < (1 << 24), result);
|
|
|
|
result <<= 8;
|
|
result += max_addr->bytes[i] - min_addr->bytes[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static AddrRange *
|
|
split_range (GstRTSPAddressPool * pool, AddrRange * range, guint skip_addr,
|
|
guint skip_port, gint n_ports)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv = pool->priv;
|
|
AddrRange *temp;
|
|
|
|
if (skip_addr) {
|
|
temp = g_slice_dup (AddrRange, range);
|
|
memcpy (temp->max.bytes, temp->min.bytes, temp->min.size);
|
|
inc_address (&temp->max, skip_addr - 1);
|
|
priv->addresses = g_list_prepend (priv->addresses, temp);
|
|
|
|
inc_address (&range->min, skip_addr);
|
|
}
|
|
|
|
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 (&temp->min, 1);
|
|
/* 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_port > 0) {
|
|
/* make a range with the skipped ports */
|
|
temp = g_slice_dup (AddrRange, range);
|
|
temp->max.port = temp->min.port + skip_port - 1;
|
|
/* and store back in pool */
|
|
priv->addresses = g_list_prepend (priv->addresses, temp);
|
|
|
|
/* increment range port */
|
|
range->min.port += skip_port;
|
|
}
|
|
/* 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
|
|
*
|
|
* 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 #GstRTSPAddress that should be freed with gst_rtsp_address_free
|
|
* after use or %NULL when no address could be acquired.
|
|
*/
|
|
GstRTSPAddress *
|
|
gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
|
|
GstRTSPAddressFlags flags, gint n_ports)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv;
|
|
GList *walk, *next;
|
|
AddrRange *result;
|
|
GstRTSPAddress *addr;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
|
|
g_return_val_if_fail (n_ports > 0, NULL);
|
|
|
|
priv = pool->priv;
|
|
result = NULL;
|
|
addr = 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;
|
|
if (flags & GST_RTSP_ADDRESS_FLAG_MULTICAST && range->ttl == 0)
|
|
continue;
|
|
if (flags & GST_RTSP_ADDRESS_FLAG_UNICAST && range->ttl != 0)
|
|
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, 0, skip, n_ports);
|
|
priv->allocated = g_list_prepend (priv->allocated, result);
|
|
break;
|
|
}
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
if (result) {
|
|
addr = g_slice_new0 (GstRTSPAddress);
|
|
addr->pool = g_object_ref (pool);
|
|
addr->address = get_address_string (&result->min);
|
|
addr->n_ports = n_ports;
|
|
addr->port = result->min.port;
|
|
addr->ttl = result->ttl;
|
|
addr->priv = result;
|
|
|
|
GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", addr->address,
|
|
addr->port, addr->ttl);
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
static void
|
|
gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
|
|
GstRTSPAddress * addr)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv;
|
|
GList *find;
|
|
AddrRange *range;
|
|
|
|
g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
|
|
g_return_if_fail (addr != NULL);
|
|
g_return_if_fail (addr->pool == pool);
|
|
|
|
priv = pool->priv;
|
|
range = addr->priv;
|
|
|
|
/* we don't want to free twice */
|
|
addr->priv = NULL;
|
|
addr->pool = NULL;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
find = g_list_find (priv->allocated, range);
|
|
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, range);
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
g_object_unref (pool);
|
|
|
|
return;
|
|
|
|
/* ERRORS */
|
|
not_found:
|
|
{
|
|
g_warning ("Released unknown address %p", addr);
|
|
g_mutex_unlock (&priv->lock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dump_range (AddrRange * range, GstRTSPAddressPool * pool)
|
|
{
|
|
gchar *addr1, *addr2;
|
|
|
|
addr1 = get_address_string (&range->min);
|
|
addr2 = get_address_string (&range->max);
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_address_pool_dump:
|
|
* @pool: a #GstRTSPAddressPool
|
|
*
|
|
* Dump the free and allocated addresses to stdout.
|
|
*/
|
|
void
|
|
gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv;
|
|
|
|
g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
|
|
|
|
priv = pool->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
g_print ("free:\n");
|
|
g_list_foreach (priv->addresses, (GFunc) dump_range, pool);
|
|
g_print ("allocated:\n");
|
|
g_list_foreach (priv->allocated, (GFunc) dump_range, pool);
|
|
g_mutex_unlock (&priv->lock);
|
|
}
|
|
|
|
static GList *
|
|
find_address_in_ranges (GList * addresses, Addr * addr, guint port,
|
|
guint n_ports, guint ttl)
|
|
{
|
|
GList *walk, *next;
|
|
|
|
/* go over available ranges */
|
|
for (walk = addresses; walk; walk = next) {
|
|
AddrRange *range;
|
|
|
|
range = walk->data;
|
|
next = walk->next;
|
|
|
|
/* Not the right type of address */
|
|
if (range->min.size != addr->size)
|
|
continue;
|
|
|
|
/* Check that the address is in the interval */
|
|
if (memcmp (range->min.bytes, addr->bytes, addr->size) > 0 ||
|
|
memcmp (range->max.bytes, addr->bytes, addr->size) < 0)
|
|
continue;
|
|
|
|
/* Make sure the requested ports are inside the range */
|
|
if (port < range->min.port || port + n_ports - 1 > range->max.port)
|
|
continue;
|
|
|
|
if (ttl != range->ttl)
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
return walk;
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_address_pool_reserve_address:
|
|
* @pool: a #GstRTSPAddressPool
|
|
* @ip_address: The IP address to reserve
|
|
* @port: The first port to reserve
|
|
* @n_ports: The number of ports
|
|
* @ttl: The requested ttl
|
|
* @address: (out) storage for a #GstRTSPAddress
|
|
*
|
|
* Take a specific address and ports from @pool. @n_ports consecutive
|
|
* ports will be allocated of which the first one can be found in
|
|
* @port.
|
|
*
|
|
* If @ttl is 0, @address should be a unicast address. If @ttl > 0, @address
|
|
* should be a valid multicast address.
|
|
*
|
|
* Returns: #GST_RTSP_ADDRESS_POOL_OK if an address was reserved. The address
|
|
* is returned in @address and should be freed with gst_rtsp_address_free
|
|
* after use.
|
|
*/
|
|
GstRTSPAddressPoolResult
|
|
gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool,
|
|
const gchar * ip_address, guint port, guint n_ports, guint ttl,
|
|
GstRTSPAddress ** address)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv;
|
|
Addr input_addr;
|
|
GList *list;
|
|
AddrRange *addr_range;
|
|
GstRTSPAddress *addr;
|
|
gboolean is_multicast;
|
|
GstRTSPAddressPoolResult result;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool),
|
|
GST_RTSP_ADDRESS_POOL_EINVAL);
|
|
g_return_val_if_fail (ip_address != NULL, GST_RTSP_ADDRESS_POOL_EINVAL);
|
|
g_return_val_if_fail (port > 0, GST_RTSP_ADDRESS_POOL_EINVAL);
|
|
g_return_val_if_fail (n_ports > 0, GST_RTSP_ADDRESS_POOL_EINVAL);
|
|
g_return_val_if_fail (address != NULL, GST_RTSP_ADDRESS_POOL_EINVAL);
|
|
|
|
priv = pool->priv;
|
|
addr_range = NULL;
|
|
addr = NULL;
|
|
is_multicast = ttl != 0;
|
|
|
|
if (!fill_address (ip_address, port, &input_addr, is_multicast))
|
|
goto invalid;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
list = find_address_in_ranges (priv->addresses, &input_addr, port, n_ports,
|
|
ttl);
|
|
if (list != NULL) {
|
|
AddrRange *range = list->data;
|
|
gint skip_port, skip_addr;
|
|
|
|
skip_addr = diff_address (&input_addr, &range->min);
|
|
skip_port = port - range->min.port;
|
|
|
|
/* we found a range, remove from the list */
|
|
priv->addresses = g_list_delete_link (priv->addresses, list);
|
|
/* now split and exit our loop */
|
|
addr_range = split_range (pool, range, skip_addr, skip_port, n_ports);
|
|
priv->allocated = g_list_prepend (priv->allocated, addr_range);
|
|
}
|
|
|
|
if (addr_range) {
|
|
addr = g_slice_new0 (GstRTSPAddress);
|
|
addr->pool = g_object_ref (pool);
|
|
addr->address = get_address_string (&addr_range->min);
|
|
addr->n_ports = n_ports;
|
|
addr->port = addr_range->min.port;
|
|
addr->ttl = addr_range->ttl;
|
|
addr->priv = addr_range;
|
|
|
|
result = GST_RTSP_ADDRESS_POOL_OK;
|
|
GST_DEBUG_OBJECT (pool, "reserved address %s:%u ttl %u", addr->address,
|
|
addr->port, addr->ttl);
|
|
} else {
|
|
/* We failed to reserve the address. Check if it was because the address
|
|
* was already in use or if it wasn't in the pool to begin with */
|
|
list = find_address_in_ranges (priv->allocated, &input_addr, port, n_ports,
|
|
ttl);
|
|
if (list != NULL) {
|
|
result = GST_RTSP_ADDRESS_POOL_ERESERVED;
|
|
} else {
|
|
result = GST_RTSP_ADDRESS_POOL_ERANGE;
|
|
}
|
|
}
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
*address = addr;
|
|
return result;
|
|
|
|
/* ERRORS */
|
|
invalid:
|
|
{
|
|
GST_ERROR_OBJECT (pool, "invalid address %s:%u/%u/%u", ip_address,
|
|
port, n_ports, ttl);
|
|
*address = NULL;
|
|
return GST_RTSP_ADDRESS_POOL_EINVAL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_rtsp_address_pool_has_unicast_addresses:
|
|
* @pool: a #GstRTSPAddressPool
|
|
*
|
|
* Used to know if the pool includes any unicast addresses.
|
|
*
|
|
* Returns: %TRUE if the pool includes any unicast addresses, %FALSE otherwise
|
|
*/
|
|
|
|
gboolean
|
|
gst_rtsp_address_pool_has_unicast_addresses (GstRTSPAddressPool * pool)
|
|
{
|
|
GstRTSPAddressPoolPrivate *priv;
|
|
gboolean has_unicast_addresses;
|
|
|
|
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
|
|
|
|
priv = pool->priv;
|
|
|
|
g_mutex_lock (&priv->lock);
|
|
has_unicast_addresses = priv->has_unicast_addresses;
|
|
g_mutex_unlock (&priv->lock);
|
|
|
|
return has_unicast_addresses;
|
|
}
|