sout: merge SAP and announce code in a single file
[vlc.git] / src / stream_output / sap.c
1 /*****************************************************************************
2  * sap.c : SAP announce handler
3  *****************************************************************************
4  * Copyright (C) 2002-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Rémi Denis-Courmont <rem # videolan.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>                                              /* sprintf() */
33 #include <string.h>
34 #include <assert.h>
35
36 #include <vlc_sout.h>
37 #include <vlc_network.h>
38
39 #include "stream_output.h"
40 #include "libvlc.h"
41
42 /* SAP is always on that port */
43 #define IPPORT_SAP 9875
44
45 struct session_descriptor_t
46 {
47     struct sockaddr_storage orig;
48     socklen_t origlen;
49     struct sockaddr_storage addr;
50     socklen_t addrlen;
51
52     char *psz_sdp;
53     bool b_ssm;
54 };
55
56 /* A SAP session descriptor, enqueued in the SAP handler queue */
57 typedef struct sap_session_t
58 {
59     struct sap_session_t *next;
60     const session_descriptor_t *p_sd;
61     size_t                length;
62     uint8_t               data[];
63 } sap_session_t;
64
65 /* A SAP announce address. For each of these, we run the
66  * control flow algorithm */
67 typedef struct sap_address_t
68 {
69     struct sap_address_t   *next;
70
71     vlc_thread_t            thread;
72     vlc_mutex_t             lock;
73     vlc_cond_t              wait;
74
75     char                    group[NI_MAXNUMERICHOST];
76     struct sockaddr_storage orig;
77     socklen_t               origlen;
78     int                     fd;
79     unsigned                interval;
80
81     unsigned                session_count;
82     sap_session_t          *first;
83 } sap_address_t;
84
85 /* The SAP handler, running in a separate thread */
86 struct sap_handler_t
87 {
88     VLC_COMMON_MEMBERS
89
90     vlc_mutex_t    lock;
91     sap_address_t *first;
92 };
93
94 #define SAP_MAX_BUFFER 65534
95 #define MIN_INTERVAL 2
96 #define MAX_INTERVAL 300
97
98 static void *RunThread (void *);
99
100 /**
101  * Create the SAP handler
102  *
103  * \param p_announce a VLC object
104  * \return the newly created SAP handler or NULL on error
105  */
106 static sap_handler_t *SAP_Create (vlc_object_t *p_announce)
107 {
108     sap_handler_t *p_sap;
109
110     p_sap = vlc_custom_create (p_announce, sizeof (*p_sap), "sap sender");
111     if (p_sap == NULL)
112         return NULL;
113
114     vlc_mutex_init (&p_sap->lock);
115     p_sap->first = NULL;
116     return p_sap;
117 }
118
119 static void SAP_Destroy (sap_handler_t *p_sap)
120 {
121     assert (p_sap->first == NULL);
122     vlc_mutex_destroy (&p_sap->lock);
123     vlc_object_release (p_sap);
124 }
125
126 static sap_address_t *AddressCreate (vlc_object_t *obj, const char *group)
127 {
128     int fd = net_ConnectUDP (obj, group, IPPORT_SAP, 255);
129     if (fd == -1)
130         return NULL;
131
132     sap_address_t *addr = malloc (sizeof (*addr));
133     if (addr == NULL)
134     {
135         net_Close (fd);
136         return NULL;
137     }
138
139     strlcpy (addr->group, group, sizeof (addr->group));
140     addr->fd = fd;
141     addr->origlen = sizeof (addr->orig);
142     getsockname (fd, (struct sockaddr *)&addr->orig, &addr->origlen);
143
144     addr->interval = var_CreateGetInteger (obj, "sap-interval");
145     vlc_mutex_init (&addr->lock);
146     vlc_cond_init (&addr->wait);
147     addr->session_count = 0;
148     addr->first = NULL;
149
150     if (vlc_clone (&addr->thread, RunThread, addr, VLC_THREAD_PRIORITY_LOW))
151     {
152         msg_Err (obj, "unable to spawn SAP announce thread");
153         net_Close (fd);
154         free (addr);
155         return NULL;
156     }
157     return addr;
158 }
159
160 static void AddressDestroy (sap_address_t *addr)
161 {
162     assert (addr->first == NULL);
163
164     vlc_cancel (addr->thread);
165     vlc_join (addr->thread, NULL);
166     vlc_cond_destroy (&addr->wait);
167     vlc_mutex_destroy (&addr->lock);
168     net_Close (addr->fd);
169     free (addr);
170 }
171
172 /**
173  * main SAP handler thread
174  * \param p_this the SAP Handler object
175  * \return nothing
176  */
177 VLC_NORETURN
178 static void *RunThread (void *self)
179 {
180     sap_address_t *addr = self;
181
182     vlc_mutex_lock (&addr->lock);
183     mutex_cleanup_push (&addr->lock);
184
185     for (;;)
186     {
187         sap_session_t *p_session;
188         mtime_t deadline;
189
190         while (addr->first == NULL)
191             vlc_cond_wait (&addr->wait, &addr->lock);
192
193         assert (addr->session_count > 0);
194
195         deadline = mdate ();
196         for (p_session = addr->first; p_session; p_session = p_session->next)
197         {
198             send (addr->fd, p_session->data, p_session->length, 0);
199             deadline += addr->interval * CLOCK_FREQ / addr->session_count;
200
201             if (vlc_cond_timedwait (&addr->wait, &addr->lock, deadline) == 0)
202                 break; /* list may have changed! */
203         }
204     }
205
206     vlc_cleanup_pop ();
207     assert (0);
208 }
209
210 /**
211  * Add a SAP announce
212  */
213 static int SAP_Add (sap_handler_t *p_sap, session_descriptor_t *p_session)
214 {
215     int i;
216     char psz_addr[NI_MAXNUMERICHOST];
217     sap_session_t *p_sap_session;
218     mtime_t i_hash;
219     union
220     {
221         struct sockaddr     a;
222         struct sockaddr_in  in;
223         struct sockaddr_in6 in6;
224     } addr;
225     socklen_t addrlen;
226
227     addrlen = p_session->addrlen;
228     if ((addrlen == 0) || (addrlen > sizeof (addr)))
229     {
230         msg_Err( p_sap, "No/invalid address specified for SAP announce" );
231         return VLC_EGENERIC;
232     }
233
234     /* Determine SAP multicast address automatically */
235     memcpy (&addr, &p_session->addr, addrlen);
236
237     switch (addr.a.sa_family)
238     {
239 #if defined (HAVE_INET_PTON) || defined (_WIN32)
240         case AF_INET6:
241         {
242             /* See RFC3513 for list of valid IPv6 scopes */
243             struct in6_addr *a6 = &addr.in6.sin6_addr;
244
245             memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
246                    "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
247             if( IN6_IS_ADDR_MULTICAST( a6 ) )
248                 /* force flags to zero, preserve scope */
249                 a6->s6_addr[1] &= 0xf;
250             else
251                 /* Unicast IPv6 - assume global scope */
252                 memcpy( a6->s6_addr, "\xff\x0e", 2 );
253             break;
254         }
255 #endif
256
257         case AF_INET:
258         {
259             /* See RFC2365 for IPv4 scopes */
260             uint32_t ipv4 = addr.in.sin_addr.s_addr;
261
262             /* 224.0.0.0/24 => 224.0.0.255 */
263             if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000))
264                 ipv4 =  htonl (0xe00000ff);
265             else
266             /* 239.255.0.0/16 => 239.255.255.255 */
267             if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000))
268                 ipv4 =  htonl (0xefffffff);
269             else
270             /* 239.192.0.0/14 => 239.195.255.255 */
271             if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000))
272                 ipv4 =  htonl (0xefc3ffff);
273             else
274             if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000))
275                 ipv4 = 0;
276             else
277             /* other addresses => 224.2.127.254 */
278                 ipv4 = htonl (0xe0027ffe);
279
280             if( ipv4 == 0 )
281             {
282                 msg_Err( p_sap, "Out-of-scope multicast address "
283                          "not supported by SAP" );
284                 return VLC_EGENERIC;
285             }
286
287             addr.in.sin_addr.s_addr = ipv4;
288             break;
289         }
290
291         default:
292             msg_Err( p_sap, "Address family %d not supported by SAP",
293                      addr.a.sa_family );
294             return VLC_EGENERIC;
295     }
296
297     i = vlc_getnameinfo( &addr.a, addrlen,
298                          psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );
299
300     if( i )
301     {
302         msg_Err( p_sap, "%s", gai_strerror( i ) );
303         return VLC_EGENERIC;
304     }
305
306     /* Find/create SAP address thread */
307     msg_Dbg( p_sap, "using SAP address: %s", psz_addr);
308
309     vlc_mutex_lock (&p_sap->lock);
310     sap_address_t *sap_addr;
311     for (sap_addr = p_sap->first; sap_addr; sap_addr = sap_addr->next)
312         if (!strcmp (psz_addr, sap_addr->group))
313             break;
314
315     if (sap_addr == NULL)
316     {
317         sap_addr = AddressCreate (VLC_OBJECT(p_sap), psz_addr);
318         if (sap_addr == NULL)
319         {
320             vlc_mutex_unlock (&p_sap->lock);
321             return VLC_EGENERIC;
322         }
323         sap_addr->next = p_sap->first;
324         p_sap->first = sap_addr;
325     }
326     /* Switch locks.
327      * NEVER take the global SAP lock when holding a SAP thread lock! */
328     vlc_mutex_lock (&sap_addr->lock);
329     vlc_mutex_unlock (&p_sap->lock);
330
331     memcpy (&p_session->orig, &sap_addr->orig, sap_addr->origlen);
332     p_session->origlen = sap_addr->origlen;
333
334     size_t headsize = 20, length;
335     switch (p_session->orig.ss_family)
336     {
337 #ifdef AF_INET6
338         case AF_INET6:
339             headsize += 16;
340             break;
341 #endif
342         case AF_INET:
343             headsize += 4;
344             break;
345         default:
346             assert (0);
347     }
348
349     /* XXX: Check for dupes */
350     length = headsize + strlen (p_session->psz_sdp);
351     p_sap_session = malloc (sizeof (*p_sap_session) + length + 1);
352     if (p_sap_session == NULL)
353     {
354         vlc_mutex_unlock (&sap_addr->lock);
355         return VLC_EGENERIC; /* NOTE: we should destroy the thread if left unused */
356     }
357     p_sap_session->next = sap_addr->first;
358     sap_addr->first = p_sap_session;
359     p_sap_session->p_sd = p_session;
360     p_sap_session->length = length;
361
362     /* Build the SAP Headers */
363     uint8_t *psz_head = p_sap_session->data;
364
365     /* SAPv1, not encrypted, not compressed */
366     psz_head[0] = 0x20;
367     psz_head[1] = 0x00; /* No authentication length */
368
369     i_hash = mdate();
370     psz_head[2] = i_hash >> 8; /* Msg id hash */
371     psz_head[3] = i_hash;      /* Msg id hash 2 */
372
373     headsize = 4;
374     switch (p_session->orig.ss_family)
375     {
376 #ifdef AF_INET6
377         case AF_INET6:
378         {
379             struct in6_addr *a6 =
380                 &((struct sockaddr_in6 *)&p_session->orig)->sin6_addr;
381             memcpy (psz_head + headsize, a6, 16);
382             psz_head[0] |= 0x10; /* IPv6 flag */
383             headsize += 16;
384             break;
385         }
386 #endif
387         case AF_INET:
388         {
389             uint32_t ipv4 =
390                 (((struct sockaddr_in *)&p_session->orig)->sin_addr.s_addr);
391             memcpy (psz_head + headsize, &ipv4, 4);
392             headsize += 4;
393             break;
394         }
395
396     }
397
398     memcpy (psz_head + headsize, "application/sdp", 16);
399     headsize += 16;
400
401     /* Build the final message */
402     strcpy( (char *)psz_head + headsize, p_session->psz_sdp);
403
404     sap_addr->session_count++;
405     vlc_cond_signal (&sap_addr->wait);
406     vlc_mutex_unlock (&sap_addr->lock);
407     return VLC_SUCCESS;
408 }
409
410 /**
411  * Remove a SAP Announce
412  */
413 static void SAP_Del (sap_handler_t *p_sap,
414                      const session_descriptor_t *p_session)
415 {
416     vlc_mutex_lock (&p_sap->lock);
417
418     /* TODO: give a handle back in SAP_Add, and use that... */
419     sap_address_t *addr, **paddr;
420     sap_session_t *session, **psession;
421
422     paddr = &p_sap->first;
423     for (addr = p_sap->first; addr; addr = addr->next)
424     {
425         psession = &addr->first;
426         vlc_mutex_lock (&addr->lock);
427         for (session = addr->first; session; session = session->next)
428         {
429             if (session->p_sd == p_session)
430                 goto found;
431             psession = &session->next;
432         }
433         vlc_mutex_unlock (&addr->lock);
434         paddr = &addr->next;
435     }
436     assert (0);
437
438 found:
439     *psession = session->next;
440
441     if (addr->first == NULL)
442         /* Last session for this address -> unlink the address */
443         *paddr = addr->next;
444     vlc_mutex_unlock (&p_sap->lock);
445
446     if (addr->first == NULL)
447     {
448         /* Last session for this address -> unlink the address */
449         vlc_mutex_unlock (&addr->lock);
450         AddressDestroy (addr);
451     }
452     else
453     {
454         addr->session_count--;
455         vlc_cond_signal (&addr->wait);
456         vlc_mutex_unlock (&addr->lock);
457     }
458
459     free (session);
460 }
461
462 /****************************************************************************
463  * Sout-side functions
464  ****************************************************************************/
465
466 static void sap_destroy (vlc_object_t *p_this)
467 {
468     libvlc_priv (p_this->p_libvlc)->p_sap = NULL;
469 }
470
471 #undef sout_AnnounceRegisterSDP
472
473 static vlc_mutex_t sap_mutex = VLC_STATIC_MUTEX;
474
475 /**
476  *  Registers a new session with the announce handler, using a pregenerated SDP
477  *
478  * \param obj a VLC object
479  * \param psz_sdp the SDP to register
480  * \param psz_dst session address (needed for SAP address auto detection)
481  * \return the new session descriptor structure
482  */
483 session_descriptor_t *
484 sout_AnnounceRegisterSDP( vlc_object_t *obj, const char *psz_sdp,
485                           const char *psz_dst )
486 {
487     session_descriptor_t *p_session = calloc( 1, sizeof (*p_session) );
488     if( !p_session )
489         return NULL;
490
491     p_session->psz_sdp = strdup( psz_sdp );
492
493     /* GRUIK. We should not convert back-and-forth from string to numbers */
494     struct addrinfo *res;
495     if (vlc_getaddrinfo (psz_dst, 0, NULL, &res) == 0)
496     {
497         if (res->ai_addrlen <= sizeof (p_session->addr))
498             memcpy (&p_session->addr, res->ai_addr,
499                     p_session->addrlen = res->ai_addrlen);
500         freeaddrinfo (res);
501     }
502
503     vlc_mutex_lock (&sap_mutex);
504     sap_handler_t *p_sap = libvlc_priv (obj->p_libvlc)->p_sap;
505     if (p_sap == NULL)
506     {
507         p_sap = SAP_Create (VLC_OBJECT (obj->p_libvlc));
508         libvlc_priv (obj->p_libvlc)->p_sap = p_sap;
509         vlc_object_set_destructor ((vlc_object_t *)p_sap, sap_destroy);
510     }
511     else
512         vlc_object_hold ((vlc_object_t *)p_sap);
513     vlc_mutex_unlock (&sap_mutex);
514
515     if (p_sap == NULL)
516         goto error;
517
518     msg_Dbg (obj, "adding SAP session");
519     if (SAP_Add (p_sap, p_session))
520     {
521         vlc_mutex_lock (&sap_mutex);
522         vlc_object_release ((vlc_object_t *)p_sap);
523         vlc_mutex_unlock (&sap_mutex);
524         goto error;
525     }
526
527     return p_session;
528
529 error:
530     free (p_session->psz_sdp);
531     free (p_session);
532     return NULL;
533 }
534
535 #undef sout_AnnounceUnRegister
536 /**
537  *  Unregisters an existing session
538  *
539  * \param obj a VLC object
540  * \param p_session the session descriptor
541  * \return VLC_SUCCESS or an error
542  */
543 int sout_AnnounceUnRegister( vlc_object_t *obj,
544                              session_descriptor_t *p_session )
545 {
546     sap_handler_t *p_sap = libvlc_priv (obj->p_libvlc)->p_sap;
547
548     msg_Dbg (obj, "removing SAP session");
549     SAP_Del (p_sap, p_session);
550
551     vlc_mutex_lock (&sap_mutex);
552     vlc_object_release ((vlc_object_t *)p_sap);
553     vlc_mutex_unlock (&sap_mutex);
554
555     free (p_session->psz_sdp);
556     free (p_session);
557
558     return 0;
559 }