2 * Copyright (C) 2012 Wim Taymans <wim.taymans at gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
23 #include "rtsp-address-pool.h"
26 * gst_rtsp_address_copy:
27 * @addr: a #GstRTSPAddress
29 * Make a copy of @addr.
31 * Returns: a copy of @addr.
34 gst_rtsp_address_copy (GstRTSPAddress * addr)
38 g_return_val_if_fail (addr != NULL, NULL);
40 copy = g_slice_dup (GstRTSPAddress, addr);
41 /* only release to the pool when the original is freed. It's a bit
42 * weird but this will do for now as it avoid us to use refcounting. */
44 copy->address = g_strdup (copy->address);
49 static void gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
50 GstRTSPAddress * addr);
53 * gst_rtsp_address_free:
54 * @addr: a #GstRTSPAddress
56 * Free @addr and releasing it back into the pool when owned by a
60 gst_rtsp_address_free (GstRTSPAddress * addr)
62 g_return_if_fail (addr != NULL);
65 /* unrefs the pool and sets it to NULL */
66 gst_rtsp_address_pool_release_address (addr->pool, addr);
68 g_free (addr->address);
69 g_slice_free (GstRTSPAddress, addr);
72 G_DEFINE_BOXED_TYPE (GstRTSPAddress, gst_rtsp_address,
73 (GBoxedCopyFunc) gst_rtsp_address_copy,
74 (GBoxedFreeFunc) gst_rtsp_address_free);
76 GST_DEBUG_CATEGORY_STATIC (rtsp_address_pool_debug);
77 #define GST_CAT_DEFAULT rtsp_address_pool_debug
79 #define GST_RTSP_ADDRESS_POOL_GET_PRIVATE(obj) \
80 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolPrivate))
82 struct _GstRTSPAddressPoolPrivate
84 GMutex lock; /* protects everything in this struct */
88 gboolean has_unicast_addresses;
91 #define ADDR_IS_IPV4(a) ((a)->size == 4)
92 #define ADDR_IS_IPV6(a) ((a)->size == 16)
93 #define ADDR_IS_EVEN_PORT(a) (((a)->port & 1) == 0)
109 #define RANGE_IS_SINGLE(r) (memcmp ((r)->min.bytes, (r)->max.bytes, (r)->min.size) == 0)
111 #define gst_rtsp_address_pool_parent_class parent_class
112 G_DEFINE_TYPE (GstRTSPAddressPool, gst_rtsp_address_pool, G_TYPE_OBJECT);
114 static void gst_rtsp_address_pool_finalize (GObject * obj);
117 gst_rtsp_address_pool_class_init (GstRTSPAddressPoolClass * klass)
119 GObjectClass *gobject_class;
121 gobject_class = G_OBJECT_CLASS (klass);
123 gobject_class->finalize = gst_rtsp_address_pool_finalize;
125 g_type_class_add_private (klass, sizeof (GstRTSPAddressPoolPrivate));
127 GST_DEBUG_CATEGORY_INIT (rtsp_address_pool_debug, "rtspaddresspool", 0,
128 "GstRTSPAddressPool");
132 gst_rtsp_address_pool_init (GstRTSPAddressPool * pool)
134 pool->priv = GST_RTSP_ADDRESS_POOL_GET_PRIVATE (pool);
136 g_mutex_init (&pool->priv->lock);
140 free_range (AddrRange * range)
142 g_slice_free (AddrRange, range);
146 gst_rtsp_address_pool_finalize (GObject * obj)
148 GstRTSPAddressPool *pool;
150 pool = GST_RTSP_ADDRESS_POOL (obj);
152 g_list_free_full (pool->priv->addresses, (GDestroyNotify) free_range);
153 g_list_free_full (pool->priv->allocated, (GDestroyNotify) free_range);
154 g_mutex_clear (&pool->priv->lock);
156 G_OBJECT_CLASS (parent_class)->finalize (obj);
160 * gst_rtsp_address_pool_new:
162 * Make a new #GstRTSPAddressPool.
164 * Returns: a new #GstRTSPAddressPool
167 gst_rtsp_address_pool_new (void)
169 GstRTSPAddressPool *pool;
171 pool = g_object_new (GST_TYPE_RTSP_ADDRESS_POOL, NULL);
177 * gst_rtsp_address_pool_clear:
178 * @pool: a #GstRTSPAddressPool
180 * Clear all addresses in @pool. There should be no outstanding
184 gst_rtsp_address_pool_clear (GstRTSPAddressPool * pool)
186 GstRTSPAddressPoolPrivate *priv;
188 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
189 g_return_if_fail (pool->priv->allocated == NULL);
193 g_mutex_lock (&priv->lock);
194 g_list_free_full (priv->addresses, (GDestroyNotify) free_range);
195 priv->addresses = NULL;
196 g_mutex_unlock (&priv->lock);
200 fill_address (const gchar * address, guint16 port, Addr * addr,
201 gboolean is_multicast)
205 inet = g_inet_address_new_from_string (address);
209 if (is_multicast != g_inet_address_get_is_multicast (inet)) {
210 g_object_unref (inet);
214 addr->size = g_inet_address_get_native_size (inet);
215 memcpy (addr->bytes, g_inet_address_to_bytes (inet), addr->size);
216 g_object_unref (inet);
223 get_address_string (Addr * addr)
228 inet = g_inet_address_new_from_bytes (addr->bytes,
229 addr->size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
230 res = g_inet_address_to_string (inet);
231 g_object_unref (inet);
237 * gst_rtsp_address_pool_add_range:
238 * @pool: a #GstRTSPAddressPool
239 * @min_address: a minimum address to add
240 * @max_address: a maximum address to add
241 * @min_port: the minimum port
242 * @max_port: the maximum port
245 * Adds the multicast addresses from @min_addess to @max_address (inclusive)
246 * to @pool. The valid port range for the addresses will be from @min_port to
247 * @max_port inclusive.
249 * Returns: %TRUE if the addresses could be added.
252 gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool,
253 const gchar * min_address, const gchar * max_address,
254 guint16 min_port, guint16 max_port, guint8 ttl)
257 GstRTSPAddressPoolPrivate *priv;
259 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
260 g_return_val_if_fail (min_port <= max_port, FALSE);
264 range = g_slice_new0 (AddrRange);
266 if (!fill_address (min_address, min_port, &range->min, (ttl != 0)))
268 if (!fill_address (max_address, max_port, &range->max, (ttl != 0)))
271 if (range->min.size != range->max.size)
273 if (memcmp (range->min.bytes, range->max.bytes, range->min.size) > 0)
278 GST_DEBUG_OBJECT (pool, "adding %s-%s:%u-%u ttl %u", min_address, max_address,
279 min_port, max_port, ttl);
281 g_mutex_lock (&priv->lock);
282 priv->addresses = g_list_prepend (priv->addresses, range);
285 priv->has_unicast_addresses = TRUE;
286 g_mutex_unlock (&priv->lock);
293 GST_ERROR_OBJECT (pool, "invalid address range %s-%s", min_address,
295 g_slice_free (AddrRange, range);
301 * gst_rtsp_address_pool_add_range_unicast:
302 * @pool: a #GstRTSPAddressPool
303 * @min_address: a minimum address to add
304 * @max_address: a maximum address to add
305 * @min_port: the minimum port
306 * @max_port: the maximum port
308 * Adds the unicast addresses from @min_addess to @max_address (inclusive)
309 * to @pool. The valid port range for the addresses will be from @min_port to
310 * @max_port inclusive.
312 * @min_address and @max_address can be set to
313 * #GST_RTSP_ADDRESS_POOL_ANY_IPV4 or #GST_RTSP_ADDRESS_POOL_ANY_IPV6 to bind
314 * to all available IPv4 or IPv6 addresses.
316 * Returns: %TRUE if the addresses could be added.
319 gst_rtsp_address_pool_add_range_unicast (GstRTSPAddressPool * pool,
320 const gchar * min_address, const gchar * max_address,
321 guint16 min_port, guint16 max_port)
323 return gst_rtsp_address_pool_add_range (pool, min_address, max_address,
324 min_port, max_port, 0);
327 /* increments the address by count */
330 inc_address (Addr * addr, guint8 count)
336 for (i = addr->size - 1; i >= 0 && carry > 0; i--) {
337 carry += addr->bytes[i];
338 addr->bytes[i] = carry & 0xff;
343 /* tells us the number of addresses between min_addr and max_addr */
346 diff_address (Addr * max_addr, Addr * min_addr)
351 g_return_val_if_fail (min_addr->size == max_addr->size, 0);
353 for (i = 0; i < min_addr->size; i++) {
354 g_return_val_if_fail (result < (1 << 24), result);
357 result += max_addr->bytes[i] - min_addr->bytes[i];
365 split_range (GstRTSPAddressPool * pool, AddrRange * range, guint skip_addr,
366 guint skip_port, gint n_ports)
368 GstRTSPAddressPoolPrivate *priv = pool->priv;
372 temp = g_slice_dup (AddrRange, range);
373 memcpy (temp->max.bytes, temp->min.bytes, temp->min.size);
374 inc_address (&temp->max, skip_addr - 1);
375 priv->addresses = g_list_prepend (priv->addresses, temp);
377 inc_address (&range->min, skip_addr);
380 if (!RANGE_IS_SINGLE (range)) {
381 /* min and max are not the same, we have more than one address. */
382 temp = g_slice_dup (AddrRange, range);
383 /* increment the range min address */
384 inc_address (&temp->min, 1);
385 /* and store back in pool */
386 priv->addresses = g_list_prepend (priv->addresses, temp);
388 /* adjust range with only the first address */
389 memcpy (range->max.bytes, range->min.bytes, range->min.size);
392 /* range now contains only one single address */
394 /* make a range with the skipped ports */
395 temp = g_slice_dup (AddrRange, range);
396 temp->max.port = temp->min.port + skip_port - 1;
397 /* and store back in pool */
398 priv->addresses = g_list_prepend (priv->addresses, temp);
400 /* increment range port */
401 range->min.port += skip_port;
403 /* range now contains single address with desired port number */
404 if (range->max.port - range->min.port + 1 > n_ports) {
405 /* make a range with the remaining ports */
406 temp = g_slice_dup (AddrRange, range);
407 temp->min.port += n_ports;
408 /* and store back in pool */
409 priv->addresses = g_list_prepend (priv->addresses, temp);
411 /* and truncate port */
412 range->max.port = range->min.port + n_ports - 1;
418 * gst_rtsp_address_pool_acquire_address:
419 * @pool: a #GstRTSPAddressPool
421 * @n_ports: the amount of ports
423 * Take an address and ports from @pool. @flags can be used to control the
424 * allocation. @n_ports consecutive ports will be allocated of which the first
425 * one can be found in @port.
427 * This function should only be used internally.
429 * Returns: a #GstRTSPAddress that should be freed with gst_rtsp_address_free
430 * after use or %NULL when no address could be acquired.
433 gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
434 GstRTSPAddressFlags flags, gint n_ports)
436 GstRTSPAddressPoolPrivate *priv;
439 GstRTSPAddress *addr;
441 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
442 g_return_val_if_fail (n_ports > 0, NULL);
448 g_mutex_lock (&priv->lock);
449 /* go over available ranges */
450 for (walk = priv->addresses; walk; walk = next) {
457 /* check address type when given */
458 if (flags & GST_RTSP_ADDRESS_FLAG_IPV4 && !ADDR_IS_IPV4 (&range->min))
460 if (flags & GST_RTSP_ADDRESS_FLAG_IPV6 && !ADDR_IS_IPV6 (&range->min))
462 if (flags & GST_RTSP_ADDRESS_FLAG_MULTICAST && range->ttl == 0)
464 if (flags & GST_RTSP_ADDRESS_FLAG_UNICAST && range->ttl != 0)
467 /* check for enough ports */
468 ports = range->max.port - range->min.port + 1;
469 if (flags & GST_RTSP_ADDRESS_FLAG_EVEN_PORT
470 && !ADDR_IS_EVEN_PORT (&range->min))
474 if (ports - skip < n_ports)
477 /* we found a range, remove from the list */
478 priv->addresses = g_list_delete_link (priv->addresses, walk);
479 /* now split and exit our loop */
480 result = split_range (pool, range, 0, skip, n_ports);
481 priv->allocated = g_list_prepend (priv->allocated, result);
484 g_mutex_unlock (&priv->lock);
487 addr = g_slice_new0 (GstRTSPAddress);
488 addr->pool = g_object_ref (pool);
489 addr->address = get_address_string (&result->min);
490 addr->n_ports = n_ports;
491 addr->port = result->min.port;
492 addr->ttl = result->ttl;
495 GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", addr->address,
496 addr->port, addr->ttl);
503 * gst_rtsp_address_pool_release_address:
504 * @pool: a #GstRTSPAddressPool
507 * Release a previously acquired address (with
508 * gst_rtsp_address_pool_acquire_address()) back into @pool.
511 gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
512 GstRTSPAddress * addr)
514 GstRTSPAddressPoolPrivate *priv;
518 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
519 g_return_if_fail (addr != NULL);
520 g_return_if_fail (addr->pool == pool);
525 /* we don't want to free twice */
529 g_mutex_lock (&priv->lock);
530 find = g_list_find (priv->allocated, range);
534 priv->allocated = g_list_delete_link (priv->allocated, find);
536 /* FIXME, merge and do something clever */
537 priv->addresses = g_list_prepend (priv->addresses, range);
538 g_mutex_unlock (&priv->lock);
540 g_object_unref (pool);
547 g_warning ("Released unknown address %p", addr);
548 g_mutex_unlock (&priv->lock);
554 dump_range (AddrRange * range, GstRTSPAddressPool * pool)
556 gchar *addr1, *addr2;
558 addr1 = get_address_string (&range->min);
559 addr2 = get_address_string (&range->max);
560 g_print (" address %s-%s, port %u-%u, ttl %u\n", addr1, addr2,
561 range->min.port, range->max.port, range->ttl);
567 * gst_rtsp_address_pool_dump:
568 * @pool: a #GstRTSPAddressPool
570 * Dump the free and allocated addresses to stdout.
573 gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool)
575 GstRTSPAddressPoolPrivate *priv;
577 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
581 g_mutex_lock (&priv->lock);
583 g_list_foreach (priv->addresses, (GFunc) dump_range, pool);
584 g_print ("allocated:\n");
585 g_list_foreach (priv->allocated, (GFunc) dump_range, pool);
586 g_mutex_unlock (&priv->lock);
590 * gst_rtsp_address_pool_reserve_address:
591 * @pool: a #GstRTSPAddressPool
592 * @address: The IP address to reserve
593 * @port: The first port to reserve
594 * @n_ports: The number of ports
595 * @ttl: The requested ttl
597 * Take a specific address and ports from @pool. @n_ports consecutive
598 * ports will be allocated of which the first one can be found in
601 * This function should only be used internally.
603 * Returns: a #GstRTSPAddress that should be freed with gst_rtsp_address_free
604 * after use or %NULL when no address could be acquired.
608 gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool,
609 const gchar * address, guint port, guint n_ports, guint ttl)
611 GstRTSPAddressPoolPrivate *priv;
615 GstRTSPAddress *addr;
617 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
618 g_return_val_if_fail (address != NULL, NULL);
619 g_return_val_if_fail (port > 0, NULL);
620 g_return_val_if_fail (n_ports > 0, NULL);
626 if (!fill_address (address, port, &input_addr, (ttl != 0))) {
627 GST_ERROR_OBJECT (pool, "invalid address %s", address);
631 g_mutex_lock (&priv->lock);
632 /* go over available ranges */
633 for (walk = priv->addresses; walk; walk = next) {
635 gint skip_port, skip_addr;
640 /* Not the right type of address */
641 if (range->min.size != input_addr.size)
644 /* Check that the address is in the interval */
645 if (memcmp (range->min.bytes, input_addr.bytes, input_addr.size) > 0 ||
646 memcmp (range->max.bytes, input_addr.bytes, input_addr.size) < 0)
649 /* Make sure the requested ports are inside the range */
650 if (port < range->min.port || port + n_ports - 1 > range->max.port)
653 if (ttl != range->ttl)
656 skip_addr = diff_address (&input_addr, &range->min);
657 skip_port = port - range->min.port;
659 /* we found a range, remove from the list */
660 priv->addresses = g_list_delete_link (priv->addresses, walk);
661 /* now split and exit our loop */
662 result = split_range (pool, range, skip_addr, skip_port, n_ports);
663 priv->allocated = g_list_prepend (priv->allocated, result);
666 g_mutex_unlock (&priv->lock);
669 addr = g_slice_new0 (GstRTSPAddress);
670 addr->pool = g_object_ref (pool);
671 addr->address = get_address_string (&result->min);
672 addr->n_ports = n_ports;
673 addr->port = result->min.port;
674 addr->ttl = result->ttl;
677 GST_DEBUG_OBJECT (pool, "reserved address %s:%u ttl %u", addr->address,
678 addr->port, addr->ttl);
685 * gst_rtsp_address_pool_has_unicast_addresses:
686 * @pool: a #GstRTSPAddressPool
688 * Used to know if the pool includes any unicast addresses.
690 * Returns: %TRUE if the pool includes any unicast addresses, %FALSE otherwise
694 gst_rtsp_address_pool_has_unicast_addresses (GstRTSPAddressPool * pool)
696 GstRTSPAddressPoolPrivate *priv;
697 gboolean has_unicast_addresses;
699 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
703 g_mutex_lock (&priv->lock);
704 has_unicast_addresses = priv->has_unicast_addresses;
705 g_mutex_unlock (&priv->lock);
707 return has_unicast_addresses;