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"
25 GST_DEBUG_CATEGORY_STATIC (rtsp_address_pool_debug);
26 #define GST_CAT_DEFAULT rtsp_address_pool_debug
28 #define GST_RTSP_ADDRESS_POOL_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolPrivate))
31 struct _GstRTSPAddressPoolPrivate
38 #define ADDR_IS_IPV4(a) ((a)->size == 4)
39 #define ADDR_IS_IPV6(a) ((a)->size == 16)
40 #define ADDR_IS_EVEN_PORT(a) (((a)->port & 1) == 0)
56 #define RANGE_IS_SINGLE(r) (memcmp ((r)->min.bytes, (r)->max.bytes, (r)->min.size) == 0)
58 #define gst_rtsp_address_pool_parent_class parent_class
59 G_DEFINE_TYPE (GstRTSPAddressPool, gst_rtsp_address_pool, G_TYPE_OBJECT);
61 static void gst_rtsp_address_pool_finalize (GObject * obj);
64 gst_rtsp_address_pool_class_init (GstRTSPAddressPoolClass * klass)
66 GObjectClass *gobject_class;
68 gobject_class = G_OBJECT_CLASS (klass);
70 gobject_class->finalize = gst_rtsp_address_pool_finalize;
72 g_type_class_add_private (klass, sizeof (GstRTSPAddressPoolPrivate));
74 GST_DEBUG_CATEGORY_INIT (rtsp_address_pool_debug, "rtspaddresspool", 0,
75 "GstRTSPAddressPool");
79 gst_rtsp_address_pool_init (GstRTSPAddressPool * pool)
81 pool->priv = GST_RTSP_ADDRESS_POOL_GET_PRIVATE (pool);
83 g_mutex_init (&pool->priv->lock);
87 free_range (AddrRange * range)
89 g_slice_free (AddrRange, range);
93 gst_rtsp_address_pool_finalize (GObject * obj)
95 GstRTSPAddressPool *pool;
97 pool = GST_RTSP_ADDRESS_POOL (obj);
99 g_list_free_full (pool->priv->addresses, (GDestroyNotify) free_range);
100 g_list_free_full (pool->priv->allocated, (GDestroyNotify) free_range);
101 g_mutex_clear (&pool->priv->lock);
103 G_OBJECT_CLASS (parent_class)->finalize (obj);
107 * gst_rtsp_address_pool_new:
109 * Make a new #GstRTSPAddressPool.
111 * Returns: a new #GstRTSPAddressPool
114 gst_rtsp_address_pool_new (void)
116 GstRTSPAddressPool *pool;
118 pool = g_object_new (GST_TYPE_RTSP_ADDRESS_POOL, NULL);
124 fill_address (const gchar * address, guint16 port, Addr * addr)
128 inet = g_inet_address_new_from_string (address);
132 addr->size = g_inet_address_get_native_size (inet);
133 memcpy (addr->bytes, g_inet_address_to_bytes (inet), addr->size);
134 g_object_unref (inet);
141 * gst_rtsp_address_pool_add_range:
142 * @pool: a #GstRTSPAddressPool
143 * @min_address: a minimum address to add
144 * @max_address: a maximum address to add
145 * @min_port: the minimum port
146 * @max_port: the maximum port
149 * Adds the multicast addresses from @min_addess to @max_address (inclusive)
150 * to @pool. The valid port range for the addresses will be from @min_port to
151 * @max_port inclusive.
153 * Returns: %TRUE if the addresses could be added.
156 gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool,
157 const gchar * min_address, const gchar * max_address,
158 guint16 min_port, guint16 max_port, guint8 ttl)
161 GstRTSPAddressPoolPrivate *priv;
163 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
164 g_return_val_if_fail (min_port <= max_port, FALSE);
168 range = g_slice_new0 (AddrRange);
170 if (!fill_address (min_address, min_port, &range->min))
172 if (!fill_address (max_address, max_port, &range->max))
175 if (range->min.size != range->max.size)
177 if (memcmp (range->min.bytes, range->max.bytes, range->min.size) > 0)
182 GST_DEBUG_OBJECT (pool, "adding %s-%s:%u-%u ttl %u", min_address, max_address,
183 min_port, max_port, ttl);
185 g_mutex_lock (&priv->lock);
186 priv->addresses = g_list_prepend (priv->addresses, range);
187 g_mutex_unlock (&priv->lock);
194 GST_ERROR_OBJECT (pool, "invalid address range %s-%s", min_address,
196 g_slice_free (AddrRange, range);
202 inc_address (GstRTSPAddressPool * pool, Addr * addr)
208 for (i = addr->size - 1; i >= 0 && carry > 0; i--) {
209 carry += addr->bytes[i];
210 addr->bytes[i] = carry & 0xff;
216 split_range (GstRTSPAddressPool * pool, AddrRange * range, gint skip,
219 GstRTSPAddressPoolPrivate *priv = pool->priv;
222 if (!RANGE_IS_SINGLE (range)) {
223 /* min and max are not the same, we have more than one address. */
224 temp = g_slice_dup (AddrRange, range);
225 /* increment the range min address */
226 inc_address (pool, &temp->min);
227 /* and store back in pool */
228 priv->addresses = g_list_prepend (priv->addresses, temp);
230 /* adjust range with only the first address */
231 memcpy (range->max.bytes, range->min.bytes, range->min.size);
234 /* range now contains only one single address */
236 /* make a range with the skipped ports */
237 temp = g_slice_dup (AddrRange, range);
238 temp->max.port = temp->min.port + skip;
239 /* and store back in pool */
240 priv->addresses = g_list_prepend (priv->addresses, temp);
242 /* increment range port */
243 range->min.port += skip;
245 /* range now contains single address with desired port number */
246 if (range->max.port - range->min.port + 1 > n_ports) {
247 /* make a range with the remaining ports */
248 temp = g_slice_dup (AddrRange, range);
249 temp->min.port += n_ports;
250 /* and store back in pool */
251 priv->addresses = g_list_prepend (priv->addresses, temp);
253 /* and truncate port */
254 range->max.port = range->min.port + n_ports - 1;
260 * gst_rtsp_address_pool_acquire_address:
261 * @pool: a #GstRTSPAddressPool
263 * @n_ports: the amount of ports
264 * @address: result address
268 * Take an address and ports from @pool. @flags can be used to control the
269 * allocation. @n_ports consecutive ports will be allocated of which the first
270 * one can be found in @port.
272 * Returns: a pointer that should be used to release the address with
273 * gst_rtsp_address_pool_release_address() after usage or %NULL when no
274 * address could be acquired.
277 gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
278 GstRTSPAddressFlags flags, gint n_ports, gchar ** address,
279 guint16 * port, guint8 * ttl)
281 GstRTSPAddressPoolPrivate *priv;
285 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
286 g_return_val_if_fail (n_ports > 0, NULL);
287 g_return_val_if_fail (address != NULL, NULL);
288 g_return_val_if_fail (port != NULL, NULL);
289 g_return_val_if_fail (ttl != NULL, NULL);
294 g_mutex_lock (&priv->lock);
295 /* go over available ranges */
296 for (walk = priv->addresses; walk; walk = next) {
303 /* check address type when given */
304 if (flags & GST_RTSP_ADDRESS_FLAG_IPV4 && !ADDR_IS_IPV4 (&range->min))
306 if (flags & GST_RTSP_ADDRESS_FLAG_IPV6 && !ADDR_IS_IPV6 (&range->min))
309 /* check for enough ports */
310 ports = range->max.port - range->min.port + 1;
311 if (flags & GST_RTSP_ADDRESS_FLAG_EVEN_PORT
312 && !ADDR_IS_EVEN_PORT (&range->min))
316 if (ports - skip < n_ports)
319 /* we found a range, remove from the list */
320 priv->addresses = g_list_delete_link (priv->addresses, walk);
321 /* now split and exit our loop */
322 result = split_range (pool, range, skip, n_ports);
323 priv->allocated = g_list_prepend (priv->allocated, result);
326 g_mutex_unlock (&priv->lock);
331 inet = g_inet_address_new_from_bytes (result->min.bytes,
332 result->min.size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
334 *address = g_inet_address_to_string (inet);
335 g_object_unref (inet);
337 *port = result->min.port;
340 GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", *address, *port, *ttl);
346 * gst_rtsp_address_pool_release_address:
347 * @pool: a #GstRTSPAddressPool
350 * Release a previously acquired address (with
351 * gst_rtsp_address_pool_acquire_address()) back into @pool.
354 gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool, gpointer id)
356 GstRTSPAddressPoolPrivate *priv;
359 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
360 g_return_if_fail (id != NULL);
364 g_mutex_lock (&priv->lock);
365 find = g_list_find (priv->allocated, id);
369 priv->allocated = g_list_delete_link (priv->allocated, find);
371 /* FIXME, merge and do something clever */
372 priv->addresses = g_list_prepend (priv->addresses, id);
373 g_mutex_unlock (&priv->lock);
380 g_mutex_unlock (&priv->lock);
386 dump_range (GstRTSPAddressPool * pool, AddrRange * range)
389 gchar *addr1, *addr2;
391 inet = g_inet_address_new_from_bytes (range->min.bytes,
392 range->max.size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
393 addr1 = g_inet_address_to_string (inet);
394 g_object_unref (inet);
396 inet = g_inet_address_new_from_bytes (range->max.bytes,
397 range->max.size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
398 addr2 = g_inet_address_to_string (inet);
399 g_object_unref (inet);
401 g_print (" address %s-%s, port %u-%u, ttl %u\n", addr1, addr2,
402 range->min.port, range->max.port, range->ttl);
409 gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool)
411 GstRTSPAddressPoolPrivate *priv;
414 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
418 g_mutex_lock (&priv->lock);
420 for (walk = priv->addresses; walk; walk = walk->next) {
421 dump_range (pool, (AddrRange *) walk->data);
423 g_print ("allocated:\n");
424 for (walk = priv->allocated; walk; walk = walk->next) {
425 dump_range (pool, (AddrRange *) walk->data);
427 g_mutex_unlock (&priv->lock);