1 /*****************************************************************************
2 * sap.c : SAP announce handler
3 *****************************************************************************
4 * Copyright (C) 2002-2008 VLC authors and VideoLAN
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 *****************************************************************************/
29 #include <vlc_common.h>
31 #include <stdlib.h> /* free() */
32 #include <stdio.h> /* sprintf() */
37 #include <vlc_network.h>
39 #include "stream_output.h"
42 /* SAP is always on that port */
43 #define IPPORT_SAP 9875
45 /* A SAP session descriptor, enqueued in the SAP handler queue */
46 struct session_descriptor_t
48 struct session_descriptor_t *next;
53 /* A SAP announce address. For each of these, we run the
54 * control flow algorithm */
55 typedef struct sap_address_t
57 struct sap_address_t *next;
63 char group[NI_MAXNUMERICHOST];
64 struct sockaddr_storage orig;
69 unsigned session_count;
70 session_descriptor_t *first;
73 /* The SAP handler, running in a separate thread */
82 #define SAP_MAX_BUFFER 65534
83 #define MIN_INTERVAL 2
84 #define MAX_INTERVAL 300
86 static void *RunThread (void *);
89 * Create the SAP handler
91 * \param p_announce a VLC object
92 * \return the newly created SAP handler or NULL on error
94 static sap_handler_t *SAP_Create (vlc_object_t *p_announce)
98 p_sap = vlc_custom_create (p_announce, sizeof (*p_sap), "sap sender");
102 vlc_mutex_init (&p_sap->lock);
107 static void SAP_Destroy (sap_handler_t *p_sap)
109 assert (p_sap->first == NULL);
110 vlc_mutex_destroy (&p_sap->lock);
111 vlc_object_release (p_sap);
114 static sap_address_t *AddressCreate (vlc_object_t *obj, const char *group)
116 int fd = net_ConnectUDP (obj, group, IPPORT_SAP, 255);
120 sap_address_t *addr = malloc (sizeof (*addr));
127 strlcpy (addr->group, group, sizeof (addr->group));
129 addr->origlen = sizeof (addr->orig);
130 getsockname (fd, (struct sockaddr *)&addr->orig, &addr->origlen);
132 addr->interval = var_CreateGetInteger (obj, "sap-interval");
133 vlc_mutex_init (&addr->lock);
134 vlc_cond_init (&addr->wait);
135 addr->session_count = 0;
138 if (vlc_clone (&addr->thread, RunThread, addr, VLC_THREAD_PRIORITY_LOW))
140 msg_Err (obj, "unable to spawn SAP announce thread");
148 static void AddressDestroy (sap_address_t *addr)
150 assert (addr->first == NULL);
152 vlc_cancel (addr->thread);
153 vlc_join (addr->thread, NULL);
154 vlc_cond_destroy (&addr->wait);
155 vlc_mutex_destroy (&addr->lock);
156 net_Close (addr->fd);
161 * main SAP handler thread
162 * \param p_this the SAP Handler object
166 static void *RunThread (void *self)
168 sap_address_t *addr = self;
170 vlc_mutex_lock (&addr->lock);
171 mutex_cleanup_push (&addr->lock);
175 session_descriptor_t *p_session;
178 while (addr->first == NULL)
179 vlc_cond_wait (&addr->wait, &addr->lock);
181 assert (addr->session_count > 0);
184 for (p_session = addr->first; p_session; p_session = p_session->next)
186 send (addr->fd, p_session->data, p_session->length, 0);
187 deadline += addr->interval * CLOCK_FREQ / addr->session_count;
189 if (vlc_cond_timedwait (&addr->wait, &addr->lock, deadline) == 0)
190 break; /* list may have changed! */
201 static session_descriptor_t *SAP_Add (sap_handler_t *p_sap,
202 const char *sdp, const char *dst)
205 char psz_addr[NI_MAXNUMERICHOST];
209 struct sockaddr_in in;
210 struct sockaddr_in6 in6;
212 socklen_t addrlen = 0;
213 struct addrinfo *res;
215 if (vlc_getaddrinfo (dst, 0, NULL, &res) == 0)
217 if (res->ai_addrlen <= sizeof (addr))
218 memcpy (&addr, res->ai_addr, res->ai_addrlen);
219 addrlen = res->ai_addrlen;
223 if (addrlen == 0 || addrlen > sizeof (addr))
225 msg_Err( p_sap, "No/invalid address specified for SAP announce" );
229 /* Determine SAP multicast address automatically */
230 switch (addr.a.sa_family)
232 #if defined (HAVE_INET_PTON) || defined (_WIN32)
235 /* See RFC3513 for list of valid IPv6 scopes */
236 struct in6_addr *a6 = &addr.in6.sin6_addr;
238 memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
239 "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
240 if( IN6_IS_ADDR_MULTICAST( a6 ) )
241 /* force flags to zero, preserve scope */
242 a6->s6_addr[1] &= 0xf;
244 /* Unicast IPv6 - assume global scope */
245 memcpy( a6->s6_addr, "\xff\x0e", 2 );
252 /* See RFC2365 for IPv4 scopes */
253 uint32_t ipv4 = addr.in.sin_addr.s_addr;
255 /* 224.0.0.0/24 => 224.0.0.255 */
256 if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000))
257 ipv4 = htonl (0xe00000ff);
259 /* 239.255.0.0/16 => 239.255.255.255 */
260 if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000))
261 ipv4 = htonl (0xefffffff);
263 /* 239.192.0.0/14 => 239.195.255.255 */
264 if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000))
265 ipv4 = htonl (0xefc3ffff);
267 if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000))
270 /* other addresses => 224.2.127.254 */
271 ipv4 = htonl (0xe0027ffe);
275 msg_Err( p_sap, "Out-of-scope multicast address "
276 "not supported by SAP" );
280 addr.in.sin_addr.s_addr = ipv4;
285 msg_Err( p_sap, "Address family %d not supported by SAP",
290 i = vlc_getnameinfo( &addr.a, addrlen,
291 psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );
295 msg_Err( p_sap, "%s", gai_strerror( i ) );
299 /* Find/create SAP address thread */
300 msg_Dbg( p_sap, "using SAP address: %s", psz_addr);
302 vlc_mutex_lock (&p_sap->lock);
303 sap_address_t *sap_addr;
304 for (sap_addr = p_sap->first; sap_addr; sap_addr = sap_addr->next)
305 if (!strcmp (psz_addr, sap_addr->group))
308 if (sap_addr == NULL)
310 sap_addr = AddressCreate (VLC_OBJECT(p_sap), psz_addr);
311 if (sap_addr == NULL)
313 vlc_mutex_unlock (&p_sap->lock);
316 sap_addr->next = p_sap->first;
317 p_sap->first = sap_addr;
320 * NEVER take the global SAP lock when holding a SAP thread lock! */
321 vlc_mutex_lock (&sap_addr->lock);
322 vlc_mutex_unlock (&p_sap->lock);
325 switch (sap_addr->orig.ss_family)
339 /* XXX: Check for dupes */
340 length += strlen (sdp);
342 session_descriptor_t *session = malloc (sizeof (*session) + length);
343 if (unlikely(session == NULL))
345 vlc_mutex_unlock (&sap_addr->lock);
346 return NULL; /* NOTE: we should destroy the thread if left unused */
348 session->next = sap_addr->first;
349 sap_addr->first = session;
350 session->length = length;
352 /* Build the SAP Headers */
353 uint8_t *buf = session->data;
355 /* SAPv1, not encrypted, not compressed */
357 buf[1] = 0x00; /* No authentication length */
358 SetWBE(buf + 2, mdate()); /* ID hash */
361 switch (sap_addr->orig.ss_family)
366 const struct in6_addr *a6 =
367 &((const struct sockaddr_in6 *)&sap_addr->orig)->sin6_addr;
368 memcpy (buf + offset, a6, 16);
369 buf[0] |= 0x10; /* IPv6 flag */
376 const struct in_addr *a4 =
377 &((const struct sockaddr_in *)&sap_addr->orig)->sin_addr;
378 memcpy (buf + offset, a4, 4);
385 memcpy (buf + offset, "application/sdp", 16);
388 /* Build the final message */
389 strcpy((char *)buf + offset, sdp);
391 sap_addr->session_count++;
392 vlc_cond_signal (&sap_addr->wait);
393 vlc_mutex_unlock (&sap_addr->lock);
398 * Remove a SAP Announce
400 static void SAP_Del (sap_handler_t *p_sap, session_descriptor_t *session)
402 sap_address_t *addr, **paddr;
403 session_descriptor_t **psession;
405 vlc_mutex_lock (&p_sap->lock);
406 paddr = &p_sap->first;
410 assert (addr != NULL);
412 psession = &addr->first;
413 vlc_mutex_lock (&addr->lock);
414 while (*psession != NULL)
416 if (*psession == session)
418 psession = &(*psession)->next;
420 vlc_mutex_unlock (&addr->lock);
425 *psession = session->next;
427 if (addr->first == NULL)
428 /* Last session for this address -> unlink the address */
430 vlc_mutex_unlock (&p_sap->lock);
432 if (addr->first == NULL)
434 /* Last session for this address -> unlink the address */
435 vlc_mutex_unlock (&addr->lock);
436 AddressDestroy (addr);
440 addr->session_count--;
441 vlc_cond_signal (&addr->wait);
442 vlc_mutex_unlock (&addr->lock);
448 /****************************************************************************
449 * Sout-side functions
450 ****************************************************************************/
452 static void sap_destroy (vlc_object_t *p_this)
454 libvlc_priv (p_this->p_libvlc)->p_sap = NULL;
457 #undef sout_AnnounceRegisterSDP
459 static vlc_mutex_t sap_mutex = VLC_STATIC_MUTEX;
462 * Registers a new session with the announce handler, using a pregenerated SDP
464 * \param obj a VLC object
465 * \param psz_sdp the SDP to register
466 * \param psz_dst session address (needed for SAP address auto detection)
467 * \return the new session descriptor structure
469 session_descriptor_t *
470 sout_AnnounceRegisterSDP( vlc_object_t *obj, const char *psz_sdp,
471 const char *psz_dst )
473 vlc_mutex_lock (&sap_mutex);
474 sap_handler_t *p_sap = libvlc_priv (obj->p_libvlc)->p_sap;
477 p_sap = SAP_Create (VLC_OBJECT (obj->p_libvlc));
478 libvlc_priv (obj->p_libvlc)->p_sap = p_sap;
479 vlc_object_set_destructor ((vlc_object_t *)p_sap, sap_destroy);
482 vlc_object_hold ((vlc_object_t *)p_sap);
483 vlc_mutex_unlock (&sap_mutex);
488 msg_Dbg (obj, "adding SAP session");
490 session_descriptor_t *session = SAP_Add (p_sap, psz_sdp, psz_dst);
493 vlc_mutex_lock (&sap_mutex);
494 vlc_object_release ((vlc_object_t *)p_sap);
495 vlc_mutex_unlock (&sap_mutex);
500 #undef sout_AnnounceUnRegister
502 * Unregisters an existing session
504 * \param obj a VLC object
505 * \param p_session the session descriptor
506 * \return VLC_SUCCESS or an error
508 int sout_AnnounceUnRegister( vlc_object_t *obj,
509 session_descriptor_t *p_session )
511 sap_handler_t *p_sap = libvlc_priv (obj->p_libvlc)->p_sap;
513 msg_Dbg (obj, "removing SAP session");
514 SAP_Del (p_sap, p_session);
516 vlc_mutex_lock (&sap_mutex);
517 vlc_object_release ((vlc_object_t *)p_sap);
518 vlc_mutex_unlock (&sap_mutex);