i18n: Add Assamese translation
[vlc.git] / src / stream_output / sap.c
blobee5312eb7d8f0001277a24752de01cd75a041139
1 /*****************************************************************************
2 * sap.c : SAP announce handler
3 *****************************************************************************
4 * Copyright (C) 2002-2008 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Rémi Denis-Courmont <rem # videolan.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <vlc_common.h>
31 #include <stdnoreturn.h>
32 #include <stdlib.h> /* free() */
33 #include <stdio.h> /* sprintf() */
34 #include <string.h>
35 #include <assert.h>
37 #include <vlc_sout.h>
38 #include <vlc_network.h>
39 #include <vlc_memstream.h>
41 #include "stream_output.h"
42 #include "libvlc.h"
44 /* SAP is always on that port */
45 #define IPPORT_SAP 9875
47 /* A SAP session descriptor, enqueued in the SAP handler queue */
48 struct session_descriptor_t
50 struct session_descriptor_t *next;
51 size_t length;
52 char *data;
55 /* A SAP announce address. For each of these, we run the
56 * control flow algorithm */
57 typedef struct sap_address_t
59 struct sap_address_t *next;
61 vlc_thread_t thread;
62 vlc_mutex_t lock;
63 vlc_cond_t wait;
65 char group[NI_MAXNUMERICHOST];
66 struct sockaddr_storage orig;
67 socklen_t origlen;
68 int fd;
69 unsigned interval;
71 unsigned session_count;
72 session_descriptor_t *first;
73 } sap_address_t;
75 static sap_address_t *sap_addrs = NULL;
76 static vlc_mutex_t sap_mutex = VLC_STATIC_MUTEX;
78 #define SAP_MAX_BUFFER 65534
79 #define MIN_INTERVAL 2
80 #define MAX_INTERVAL 300
82 static void *RunThread (void *);
84 static sap_address_t *AddressCreate (vlc_object_t *obj, const char *group)
86 int fd = net_ConnectUDP (obj, group, IPPORT_SAP, 255);
87 if (fd == -1)
88 return NULL;
90 sap_address_t *addr = malloc (sizeof (*addr));
91 if (addr == NULL)
93 net_Close (fd);
94 return NULL;
97 strlcpy (addr->group, group, sizeof (addr->group));
98 addr->fd = fd;
99 addr->origlen = sizeof (addr->orig);
100 getsockname (fd, (struct sockaddr *)&addr->orig, &addr->origlen);
102 addr->interval = var_CreateGetInteger (obj, "sap-interval");
103 vlc_mutex_init (&addr->lock);
104 vlc_cond_init (&addr->wait);
105 addr->session_count = 0;
106 addr->first = NULL;
108 if (vlc_clone (&addr->thread, RunThread, addr, VLC_THREAD_PRIORITY_LOW))
110 msg_Err (obj, "unable to spawn SAP announce thread");
111 net_Close (fd);
112 free (addr);
113 return NULL;
115 return addr;
118 static void AddressDestroy (sap_address_t *addr)
120 assert (addr->first == NULL);
122 vlc_cancel (addr->thread);
123 vlc_join (addr->thread, NULL);
124 vlc_cond_destroy (&addr->wait);
125 vlc_mutex_destroy (&addr->lock);
126 net_Close (addr->fd);
127 free (addr);
131 * main SAP handler thread
132 * \param p_this the SAP Handler object
133 * \return nothing
135 noreturn static void *RunThread (void *self)
137 sap_address_t *addr = self;
139 vlc_mutex_lock (&addr->lock);
140 mutex_cleanup_push (&addr->lock);
142 for (;;)
144 session_descriptor_t *p_session;
145 mtime_t deadline;
147 while (addr->first == NULL)
148 vlc_cond_wait (&addr->wait, &addr->lock);
150 assert (addr->session_count > 0);
152 deadline = mdate ();
153 for (p_session = addr->first; p_session; p_session = p_session->next)
155 send (addr->fd, p_session->data, p_session->length, 0);
156 deadline += addr->interval * CLOCK_FREQ / addr->session_count;
158 if (vlc_cond_timedwait (&addr->wait, &addr->lock, deadline) == 0)
159 break; /* list may have changed! */
163 vlc_cleanup_pop ();
164 vlc_assert_unreachable ();
167 #undef sout_AnnounceRegisterSDP
169 * Registers a new session with the announce handler, using a pregenerated SDP
171 * \param obj a VLC object
172 * \param sdp the SDP to register
173 * \param dst session address (needed for SAP address auto detection)
174 * \return the new session descriptor structure
176 session_descriptor_t *
177 sout_AnnounceRegisterSDP (vlc_object_t *obj, const char *sdp,
178 const char *dst)
180 int i;
181 char psz_addr[NI_MAXNUMERICHOST];
182 union
184 struct sockaddr a;
185 struct sockaddr_in in;
186 struct sockaddr_in6 in6;
187 } addr;
188 socklen_t addrlen = 0;
189 struct addrinfo *res;
191 msg_Dbg (obj, "adding SAP session");
193 if (vlc_getaddrinfo (dst, 0, NULL, &res) == 0)
195 if (res->ai_addrlen <= sizeof (addr))
196 memcpy (&addr, res->ai_addr, res->ai_addrlen);
197 addrlen = res->ai_addrlen;
198 freeaddrinfo (res);
201 if (addrlen == 0 || addrlen > sizeof (addr))
203 msg_Err (obj, "No/invalid address specified for SAP announce" );
204 return NULL;
207 /* Determine SAP multicast address automatically */
208 switch (addr.a.sa_family)
210 #if defined (HAVE_INET_PTON) || defined (_WIN32)
211 case AF_INET6:
213 /* See RFC3513 for list of valid IPv6 scopes */
214 struct in6_addr *a6 = &addr.in6.sin6_addr;
216 memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
217 "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
218 if( IN6_IS_ADDR_MULTICAST( a6 ) )
219 /* force flags to zero, preserve scope */
220 a6->s6_addr[1] &= 0xf;
221 else
222 /* Unicast IPv6 - assume global scope */
223 memcpy( a6->s6_addr, "\xff\x0e", 2 );
224 break;
226 #endif
228 case AF_INET:
230 /* See RFC2365 for IPv4 scopes */
231 uint32_t ipv4 = addr.in.sin_addr.s_addr;
233 /* 224.0.0.0/24 => 224.0.0.255 */
234 if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000))
235 ipv4 = htonl (0xe00000ff);
236 else
237 /* 239.255.0.0/16 => 239.255.255.255 */
238 if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000))
239 ipv4 = htonl (0xefffffff);
240 else
241 /* 239.192.0.0/14 => 239.195.255.255 */
242 if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000))
243 ipv4 = htonl (0xefc3ffff);
244 else
245 if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000))
246 ipv4 = 0;
247 else
248 /* other addresses => 224.2.127.254 */
249 ipv4 = htonl (0xe0027ffe);
251 if( ipv4 == 0 )
253 msg_Err (obj, "Out-of-scope multicast address "
254 "not supported by SAP");
255 return NULL;
258 addr.in.sin_addr.s_addr = ipv4;
259 break;
262 default:
263 msg_Err (obj, "Address family %d not supported by SAP",
264 addr.a.sa_family);
265 return NULL;
268 i = vlc_getnameinfo( &addr.a, addrlen,
269 psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );
271 if( i )
273 msg_Err (obj, "%s", gai_strerror (i));
274 return NULL;
277 /* Find/create SAP address thread */
278 sap_address_t *sap_addr;
280 msg_Dbg (obj, "using SAP address: %s", psz_addr);
281 vlc_mutex_lock (&sap_mutex);
282 for (sap_addr = sap_addrs; sap_addr; sap_addr = sap_addr->next)
283 if (!strcmp (psz_addr, sap_addr->group))
284 break;
286 if (sap_addr == NULL)
288 sap_addr = AddressCreate (obj, psz_addr);
289 if (sap_addr == NULL)
291 vlc_mutex_unlock (&sap_mutex);
292 return NULL;
294 sap_addr->next = sap_addrs;
295 sap_addrs = sap_addr;
297 /* Switch locks.
298 * NEVER take the global SAP lock when holding a SAP thread lock! */
299 vlc_mutex_lock (&sap_addr->lock);
300 vlc_mutex_unlock (&sap_mutex);
302 session_descriptor_t *session = malloc(sizeof (*session));
303 if (unlikely(session == NULL))
304 goto out; /* NOTE: we should destroy the thread if left unused */
306 session->next = sap_addr->first;
308 /* Build the SAP Headers */
309 struct vlc_memstream stream;
310 vlc_memstream_open(&stream);
312 /* SAPv1, not encrypted, not compressed */
313 uint8_t flags = 0x20;
314 #ifdef AF_INET6
315 if (sap_addr->orig.ss_family == AF_INET6)
316 flags |= 0x10;
317 #endif
318 vlc_memstream_putc(&stream, flags);
319 vlc_memstream_putc(&stream, 0x00); /* No authentication length */
320 vlc_memstream_write(&stream, &(uint16_t){ mdate() }, 2); /* ID hash */
322 switch (sap_addr->orig.ss_family)
324 #ifdef AF_INET6
325 case AF_INET6:
327 const struct in6_addr *a6 =
328 &((const struct sockaddr_in6 *)&sap_addr->orig)->sin6_addr;
329 vlc_memstream_write(&stream, &a6, 16);
330 break;
332 #endif
333 case AF_INET:
335 const struct in_addr *a4 =
336 &((const struct sockaddr_in *)&sap_addr->orig)->sin_addr;
337 vlc_memstream_write(&stream, &a4, 4);
338 break;
340 default:
341 vlc_assert_unreachable ();
344 vlc_memstream_puts(&stream, "application/sdp");
345 vlc_memstream_putc(&stream, '\0');
347 /* Build the final message */
348 vlc_memstream_puts(&stream, sdp);
350 if (vlc_memstream_close(&stream))
352 free(session);
353 session = NULL;
354 goto out;
357 session->data = stream.ptr;
358 session->length = stream.length;
359 sap_addr->first = session;
360 sap_addr->session_count++;
361 vlc_cond_signal (&sap_addr->wait);
362 out:
363 vlc_mutex_unlock (&sap_addr->lock);
364 return session;
367 #undef sout_AnnounceUnRegister
369 * Unregisters an existing session
371 * \param obj a VLC object
372 * \param session the session descriptor
374 void sout_AnnounceUnRegister (vlc_object_t *obj, session_descriptor_t *session)
376 sap_address_t *addr, **paddr;
377 session_descriptor_t **psession;
379 msg_Dbg (obj, "removing SAP session");
380 vlc_mutex_lock (&sap_mutex);
381 paddr = &sap_addrs;
382 for (;;)
384 addr = *paddr;
385 assert (addr != NULL);
387 psession = &addr->first;
388 vlc_mutex_lock (&addr->lock);
389 while (*psession != NULL)
391 if (*psession == session)
392 goto found;
393 psession = &(*psession)->next;
395 vlc_mutex_unlock (&addr->lock);
396 paddr = &addr->next;
399 found:
400 *psession = session->next;
402 if (addr->first == NULL)
403 /* Last session for this address -> unlink the address */
404 *paddr = addr->next;
405 vlc_mutex_unlock (&sap_mutex);
407 if (addr->first == NULL)
409 /* Last session for this address -> unlink the address */
410 vlc_mutex_unlock (&addr->lock);
411 AddressDestroy (addr);
413 else
415 addr->session_count--;
416 vlc_cond_signal (&addr->wait);
417 vlc_mutex_unlock (&addr->lock);
420 free(session->data);
421 free(session);