access: srt: add support stream encryption
[vlc.git] / modules / services_discovery / sap.c
blob3bcdeae3bb1f45b59e7834f61fe2b186a6248156
1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2004-2005 the VideoLAN team
5 * Copyright © 2007 Rémi Denis-Courmont
6 * $Id$
8 * Authors: Clément Stenac <zorglub@videolan.org>
9 * Rémi Denis-Courmont
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
27 * Includes
28 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <assert.h>
38 #include <vlc_demux.h>
39 #include <vlc_services_discovery.h>
41 #include <vlc_network.h>
42 #include <vlc_charset.h>
44 #include <errno.h>
45 #include <unistd.h>
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_POLL
50 # include <poll.h>
51 #endif
53 #ifdef HAVE_ZLIB_H
54 # include <zlib.h>
55 #endif
57 #ifndef _WIN32
58 # include <net/if.h>
59 #endif
61 /************************************************************************
62 * Macros and definitions
63 ************************************************************************/
65 #define MAX_LINE_LENGTH 256
67 /* SAP is always on that port */
68 #define SAP_PORT 9875
69 /* Global-scope SAP address */
70 #define SAP_V4_GLOBAL_ADDRESS "224.2.127.254"
71 /* Organization-local SAP address */
72 #define SAP_V4_ORG_ADDRESS "239.195.255.255"
73 /* Local (smallest non-link-local scope) SAP address */
74 #define SAP_V4_LOCAL_ADDRESS "239.255.255.255"
75 /* Link-local SAP address */
76 #define SAP_V4_LINK_ADDRESS "224.0.0.255"
77 #define ADD_SESSION 1
79 /*****************************************************************************
80 * Module descriptor
81 *****************************************************************************/
82 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
83 #define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \
84 "right addresses to listen to. However, you " \
85 "can specify a specific address." )
86 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
87 #define SAP_TIMEOUT_LONGTEXT N_( \
88 "Delay after which SAP items get deleted if no new announcement " \
89 "is received." )
90 #define SAP_PARSE_TEXT N_( "Try to parse the announce" )
91 #define SAP_PARSE_LONGTEXT N_( \
92 "This enables actual parsing of the announces by the SAP module. " \
93 "Otherwise, all announcements are parsed by the \"live555\" " \
94 "(RTP/RTSP) module." )
95 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
96 #define SAP_STRICT_LONGTEXT N_( \
97 "When this is set, the SAP parser will discard some non-compliant " \
98 "announcements." )
100 /* Callbacks */
101 static int Open ( vlc_object_t * );
102 static void Close( vlc_object_t * );
103 static int OpenDemux ( vlc_object_t * );
104 static void CloseDemux ( vlc_object_t * );
106 VLC_SD_PROBE_HELPER("sap", N_("Network streams (SAP)"), SD_CAT_LAN)
108 vlc_module_begin ()
109 set_shortname( N_("SAP"))
110 set_description( N_("Network streams (SAP)") )
111 set_category( CAT_PLAYLIST )
112 set_subcategory( SUBCAT_PLAYLIST_SD )
114 add_string( "sap-addr", NULL,
115 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, true )
116 add_obsolete_bool( "sap-ipv4" ) /* since 2.0.0 */
117 add_obsolete_bool( "sap-ipv6" ) /* since 2.0.0 */
118 add_integer( "sap-timeout", 1800,
119 SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, true )
120 add_bool( "sap-parse", true,
121 SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, true )
122 add_bool( "sap-strict", false,
123 SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, true )
124 add_obsolete_bool( "sap-timeshift" ) /* Redumdant since 1.0.0 */
126 set_capability( "services_discovery", 0 )
127 set_callbacks( Open, Close )
129 VLC_SD_PROBE_SUBMODULE
131 add_submodule ()
132 set_description( N_("SDP Descriptions parser") )
133 add_shortcut( "sdp" )
134 set_capability( "demux", 51 )
135 set_callbacks( OpenDemux, CloseDemux )
136 vlc_module_end ()
139 /*****************************************************************************
140 * Local structures
141 *****************************************************************************/
143 typedef struct sdp_t sdp_t;
144 typedef struct attribute_t attribute_t;
145 typedef struct sap_announce_t sap_announce_t;
148 struct sdp_media_t
150 struct sdp_t *parent;
151 char *fmt;
152 struct sockaddr_storage addr;
153 socklen_t addrlen;
154 unsigned n_addr;
155 int i_attributes;
156 attribute_t **pp_attributes;
160 /* The structure that contains sdp information */
161 struct sdp_t
163 const char *psz_sdp;
165 /* o field */
166 char username[64];
167 uint64_t session_id;
168 uint64_t session_version;
169 unsigned orig_ip_version;
170 char orig_host[1024];
172 /* s= field */
173 char *psz_sessionname;
175 /* i= field */
176 char *psz_sessioninfo;
178 /* old cruft */
179 /* "computed" URI */
180 char *psz_uri;
181 int i_media_type;
182 unsigned rtcp_port;
184 /* a= global attributes */
185 int i_attributes;
186 attribute_t **pp_attributes;
188 /* medias (well, we only support one atm) */
189 unsigned mediac;
190 struct sdp_media_t *mediav;
193 struct attribute_t
195 const char *value;
196 char name[];
199 struct sap_announce_t
201 mtime_t i_last;
202 mtime_t i_period;
203 uint8_t i_period_trust;
205 uint16_t i_hash;
206 uint32_t i_source[4];
208 /* SAP annnounces must only contain one SDP */
209 sdp_t *p_sdp;
211 input_item_t * p_item;
214 struct services_discovery_sys_t
216 vlc_thread_t thread;
218 /* Socket descriptors */
219 int i_fd;
220 int *pi_fd;
222 /* Table of announces */
223 int i_announces;
224 struct sap_announce_t **pp_announces;
226 /* Modes */
227 bool b_strict;
228 bool b_parse;
230 int i_timeout;
233 struct demux_sys_t
235 sdp_t *p_sdp;
238 /*****************************************************************************
239 * Local prototypes
240 *****************************************************************************/
243 /* Main functions */
244 static int Demux( demux_t *p_demux );
245 static int Control( demux_t *, int, va_list );
246 static void *Run ( void *p_sd );
248 /* Main parsing functions */
249 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
250 static int ParseSAP( services_discovery_t *p_sd, const uint8_t *p_buffer, size_t i_read );
251 static sdp_t *ParseSDP (vlc_object_t *p_sd, const char *psz_sdp);
252 static sap_announce_t *CreateAnnounce( services_discovery_t *, uint32_t *, uint16_t, sdp_t * );
253 static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
255 /* Helper functions */
256 static inline attribute_t *MakeAttribute (const char *str);
257 static const char *GetAttribute (attribute_t **tab, unsigned n, const char *name);
258 static inline void FreeAttribute (attribute_t *a);
259 static const char *FindAttribute (const sdp_t *sdp, unsigned media,
260 const char *name);
262 static bool IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
263 static int InitSocket( services_discovery_t *p_sd, const char *psz_address, int i_port );
264 static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len );
265 static void FreeSDP( sdp_t *p_sdp );
267 static inline int min_int( int a, int b )
269 return a > b ? b : a;
272 static bool IsWellKnownPayload (int type)
274 switch (type)
275 { /* Should be in sync with modules/demux/rtp.c */
276 case 0: /* PCMU/8000 */
277 case 3:
278 case 8: /* PCMA/8000 */
279 case 10: /* L16/44100/2 */
280 case 11: /* L16/44100 */
281 case 12:
282 case 14: /* MPA/90000 */
283 case 32: /* MPV/90000 */
284 case 33: /* MP2/90000 */
285 return true;
287 return false;
290 /*****************************************************************************
291 * Open: initialize and create stuff
292 *****************************************************************************/
293 static int Open( vlc_object_t *p_this )
295 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
296 services_discovery_sys_t *p_sys = (services_discovery_sys_t *)
297 malloc( sizeof( services_discovery_sys_t ) );
298 if( !p_sys )
299 return VLC_ENOMEM;
301 p_sys->i_timeout = var_CreateGetInteger( p_sd, "sap-timeout" );
303 p_sd->p_sys = p_sys;
304 p_sd->description = _("Network streams (SAP)");
306 p_sys->pi_fd = NULL;
307 p_sys->i_fd = 0;
309 p_sys->b_strict = var_CreateGetBool( p_sd, "sap-strict");
310 p_sys->b_parse = var_CreateGetBool( p_sd, "sap-parse" );
312 p_sys->i_announces = 0;
313 p_sys->pp_announces = NULL;
314 /* TODO: create sockets here, and fix racy sockets table */
315 if (vlc_clone (&p_sys->thread, Run, p_sd, VLC_THREAD_PRIORITY_LOW))
317 free (p_sys);
318 return VLC_EGENERIC;
321 return VLC_SUCCESS;
324 /*****************************************************************************
325 * OpenDemux: initialize and create stuff
326 *****************************************************************************/
327 static int OpenDemux( vlc_object_t *p_this )
329 demux_t *p_demux = (demux_t *)p_this;
330 const uint8_t *p_peek;
331 char *psz_sdp = NULL;
332 sdp_t *p_sdp = NULL;
333 int errval = VLC_EGENERIC;
334 size_t i_len;
336 if( !var_CreateGetBool( p_demux, "sap-parse" ) )
338 /* We want livedotcom module to parse this SDP file */
339 return VLC_EGENERIC;
342 assert( p_demux->s ); /* this is NOT an access_demux */
344 /* Probe for SDP */
345 if( vlc_stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
346 return VLC_EGENERIC;
348 if( memcmp( p_peek, "v=0\r\no=", 7 ) && memcmp( p_peek, "v=0\no=", 6 ) )
349 return VLC_EGENERIC;
351 /* Gather the complete sdp file */
352 for( i_len = 0, psz_sdp = NULL; i_len < 65536; )
354 const int i_read_max = 1024;
355 char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max + 1 );
356 size_t i_read;
357 if( psz_sdp_new == NULL )
359 errval = VLC_ENOMEM;
360 goto error;
362 psz_sdp = psz_sdp_new;
364 i_read = vlc_stream_Read( p_demux->s, &psz_sdp[i_len], i_read_max );
365 if( (int)i_read < 0 )
367 msg_Err( p_demux, "cannot read SDP" );
368 goto error;
370 i_len += i_read;
372 psz_sdp[i_len] = '\0';
374 if( (int)i_read < i_read_max )
375 break; // EOF
378 p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
380 if( !p_sdp )
382 msg_Warn( p_demux, "invalid SDP");
383 goto error;
386 if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
388 p_sdp->psz_uri = NULL;
390 if (!IsWellKnownPayload (p_sdp->i_media_type))
391 goto error;
392 if( p_sdp->psz_uri == NULL ) goto error;
394 demux_sys_t *p_sys = malloc( sizeof(*p_sys) );
395 if( unlikely(p_sys == NULL) )
396 goto error;
397 p_sys->p_sdp = p_sdp;
398 p_demux->p_sys = p_sys;
399 p_demux->pf_control = Control;
400 p_demux->pf_demux = Demux;
402 FREENULL( psz_sdp );
403 return VLC_SUCCESS;
405 error:
406 FREENULL( psz_sdp );
407 if( p_sdp ) FreeSDP( p_sdp );
408 return errval;
411 /*****************************************************************************
412 * Close:
413 *****************************************************************************/
414 static void Close( vlc_object_t *p_this )
416 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
417 services_discovery_sys_t *p_sys = p_sd->p_sys;
418 int i;
420 vlc_cancel (p_sys->thread);
421 vlc_join (p_sys->thread, NULL);
423 for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
425 net_Close( p_sys->pi_fd[i] );
427 FREENULL( p_sys->pi_fd );
429 for( i = p_sys->i_announces - 1; i>= 0; i-- )
431 RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
433 FREENULL( p_sys->pp_announces );
435 free( p_sys );
438 /*****************************************************************************
439 * CloseDemux: Close the demuxer
440 *****************************************************************************/
441 static void CloseDemux( vlc_object_t *p_this )
443 demux_t *p_demux = (demux_t *)p_this;
444 demux_sys_t *sys = p_demux->p_sys;
446 if( sys->p_sdp )
447 FreeSDP( sys->p_sdp );
448 free( sys );
451 /*****************************************************************************
452 * Run: main SAP thread
453 *****************************************************************************
454 * Listens to SAP packets, and sends them to packet_handle
455 *****************************************************************************/
456 #define MAX_SAP_BUFFER 5000
458 static void *Run( void *data )
460 services_discovery_t *p_sd = data;
461 char *psz_addr;
462 int timeout = -1;
463 int canc = vlc_savecancel ();
465 /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
466 * DNS queries, even if the DNS server returns an error with milliseconds.
467 * You don't want to know why the bug (as of XP SP2) wasn't fixed since
468 * Winsock 1.1 from Windows 95, if not Windows 3.1.
469 * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
470 * we have to open sockets in Run() rather than Open(). */
471 InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT );
472 InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT );
473 InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT );
474 InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT );
476 char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%";
477 #ifndef _WIN32
478 struct if_nameindex *l = if_nameindex ();
479 if (l != NULL)
481 char *ptr = strchr (psz_address, '%') + 1;
482 for (unsigned i = 0; l[i].if_index; i++)
484 strcpy (ptr, l[i].if_name);
485 InitSocket (p_sd, psz_address, SAP_PORT);
487 if_freenameindex (l);
489 #else
490 /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
491 which if_nameindex uses internally anyway */
493 // first create a dummy socket to pin down the protocol family
494 SOCKET s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
495 if( s != INVALID_SOCKET )
497 INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces
498 DWORD len = sizeof(ifaces);
500 if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) )
502 unsigned ifcount = len/sizeof(INTERFACE_INFO);
503 char *ptr = strchr (psz_address, '%') + 1;
504 for(unsigned i = 1; i<=ifcount; ++i )
506 // append link-local zone identifier
507 sprintf(ptr, "%d", i);
510 closesocket(s);
512 #endif
513 *strchr (psz_address, '%') = '\0';
515 static const char ipv6_scopes[] = "1456789ABCDE";
516 for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++)
518 psz_address[3] = *c_scope;
519 InitSocket( p_sd, psz_address, SAP_PORT );
522 psz_addr = var_CreateGetString( p_sd, "sap-addr" );
523 if( psz_addr && *psz_addr )
524 InitSocket( p_sd, psz_addr, SAP_PORT );
525 free( psz_addr );
527 if( p_sd->p_sys->i_fd == 0 )
529 msg_Err( p_sd, "unable to listen on any address" );
530 return NULL;
533 /* read SAP packets */
534 for (;;)
536 vlc_restorecancel (canc);
537 unsigned n = p_sd->p_sys->i_fd;
538 struct pollfd ufd[n];
540 for (unsigned i = 0; i < n; i++)
542 ufd[i].fd = p_sd->p_sys->pi_fd[i];
543 ufd[i].events = POLLIN;
544 ufd[i].revents = 0;
547 int val = poll (ufd, n, timeout);
548 canc = vlc_savecancel ();
549 if (val > 0)
551 for (unsigned i = 0; i < n; i++)
553 if (ufd[i].revents)
555 uint8_t p_buffer[MAX_SAP_BUFFER+1];
556 ssize_t i_read;
558 i_read = recv (ufd[i].fd, p_buffer, MAX_SAP_BUFFER, 0);
559 if (i_read < 0)
560 msg_Warn (p_sd, "receive error: %s",
561 vlc_strerror_c(errno));
562 if (i_read > 6)
564 /* Parse the packet */
565 p_buffer[i_read] = '\0';
566 ParseSAP (p_sd, p_buffer, i_read);
572 mtime_t now = mdate();
574 /* A 1 hour timeout correspond to the RFC Implicit timeout.
575 * This timeout is tuned in the following loop. */
576 timeout = 1000 * 60 * 60;
578 /* Check for items that need deletion */
579 for( int i = 0; i < p_sd->p_sys->i_announces; i++ )
581 mtime_t i_timeout = ( mtime_t ) 1000000 * p_sd->p_sys->i_timeout;
582 sap_announce_t * p_announce = p_sd->p_sys->pp_announces[i];
583 mtime_t i_last_period = now - p_announce->i_last;
585 /* Remove the announcement, if the last announcement was 1 hour ago
586 * or if the last packet emitted was 10 times the average time
587 * between two packets */
588 if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) ||
589 i_last_period > i_timeout )
591 RemoveAnnounce( p_sd, p_announce );
593 else
595 /* Compute next timeout */
596 if( p_announce->i_period_trust > 5 )
597 timeout = min_int((10 * p_announce->i_period - i_last_period) / 1000, timeout);
598 timeout = min_int((i_timeout - i_last_period)/1000, timeout);
602 if( !p_sd->p_sys->i_announces )
603 timeout = -1; /* We can safely poll indefinitely. */
604 else if( timeout < 200 )
605 timeout = 200; /* Don't wakeup too fast. */
607 vlc_assert_unreachable ();
610 /**********************************************************************
611 * Demux: reads and demuxes data packets
612 * Return -1 if error, 0 if EOF, 1 else
613 **********************************************************************/
614 static int Demux( demux_t *p_demux )
616 demux_sys_t *p_sys = p_demux->p_sys;
617 sdp_t *p_sdp = p_sys->p_sdp;
618 input_thread_t *p_input = p_demux->p_input;
619 input_item_t *p_parent_input;
621 if( !p_input )
623 msg_Err( p_demux, "parent input could not be found" );
624 return VLC_EGENERIC;
627 /* This item hasn't been held by input_GetItem
628 * don't release it */
629 p_parent_input = input_GetItem( p_input );
631 input_item_SetURI( p_parent_input, p_sdp->psz_uri );
632 input_item_SetName( p_parent_input, p_sdp->psz_sessionname );
633 if( p_sdp->rtcp_port )
635 char *rtcp;
636 if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 )
638 input_item_AddOption( p_parent_input, rtcp, VLC_INPUT_OPTION_TRUSTED );
639 free( rtcp );
643 vlc_mutex_lock( &p_parent_input->lock );
645 p_parent_input->i_type = ITEM_TYPE_STREAM;
646 p_parent_input->b_net = true;
648 vlc_mutex_unlock( &p_parent_input->lock );
649 return VLC_SUCCESS;
652 static int Control( demux_t *p_demux, int i_query, va_list args )
654 VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
655 return VLC_EGENERIC;
658 /**************************************************************
659 * Local functions
660 **************************************************************/
662 /* i_read is at least > 6 */
663 static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
664 size_t len )
666 const char *psz_sdp;
667 const uint8_t *end = buf + len;
668 sdp_t *p_sdp;
669 uint32_t i_source[4];
671 assert (buf[len] == '\0');
673 if (len < 4)
674 return VLC_EGENERIC;
676 uint8_t flags = buf[0];
677 uint8_t auth_len = buf[1];
679 /* First, check the sap announce is correct */
680 if ((flags >> 5) != 1)
681 return VLC_EGENERIC;
683 bool b_ipv6 = (flags & 0x10) != 0;
684 bool b_need_delete = (flags & 0x04) != 0;
686 if (flags & 0x02)
688 msg_Dbg( p_sd, "encrypted packet, unsupported" );
689 return VLC_EGENERIC;
692 bool b_compressed = (flags & 0x01) != 0;
694 uint16_t i_hash = U16_AT (buf + 2);
696 if( p_sd->p_sys->b_strict && i_hash == 0 )
698 msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
699 return VLC_EGENERIC;
702 buf += 4;
703 if( b_ipv6 )
705 for( int i = 0; i < 4; i++,buf+=4)
706 i_source[i] = U32_AT(buf);
708 else
710 memset(i_source, 0, sizeof(i_source));
711 i_source[3] = U32_AT(buf);
712 buf+=4;
714 // Skips auth data
715 buf += auth_len;
716 if (buf > end)
717 return VLC_EGENERIC;
719 uint8_t *decomp = NULL;
720 if( b_compressed )
722 int newsize = Decompress (buf, &decomp, end - buf);
723 if (newsize < 0)
725 msg_Dbg( p_sd, "decompression of SAP packet failed" );
726 return VLC_EGENERIC;
729 decomp = xrealloc (decomp, newsize + 1);
730 decomp[newsize] = '\0';
731 psz_sdp = (const char *)decomp;
732 len = newsize;
734 else
736 psz_sdp = (const char *)buf;
737 len = end - buf;
740 /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
741 assert( psz_sdp[len] == '\0');
743 /* Skip payload type */
744 /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
745 if (strncmp (psz_sdp, "v=0", 3))
747 size_t clen = strlen (psz_sdp) + 1;
749 if (strcmp (psz_sdp, "application/sdp"))
751 msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp);
752 goto error;
755 // skips content type
756 if (len <= clen)
757 goto error;
759 len -= clen;
760 psz_sdp += clen;
763 /* Parse SDP info */
764 p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
766 if( p_sdp == NULL )
767 goto error;
769 p_sdp->psz_sdp = psz_sdp;
771 /* Decide whether we should add a playlist item for this SDP */
772 /* Parse connection information (c= & m= ) */
773 if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
774 p_sdp->psz_uri = NULL;
776 /* Multi-media or no-parse -> pass to LIVE.COM */
777 if( !IsWellKnownPayload( p_sdp->i_media_type ) || !p_sd->p_sys->b_parse )
779 free( p_sdp->psz_uri );
780 if (asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp ) == -1)
781 p_sdp->psz_uri = NULL;
784 if( p_sdp->psz_uri == NULL )
786 FreeSDP( p_sdp );
787 goto error;
790 for( int i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
792 sap_announce_t * p_announce = p_sd->p_sys->pp_announces[i];
793 /* FIXME: slow */
794 if( ( !i_hash && IsSameSession( p_announce->p_sdp, p_sdp ) )
795 || ( i_hash && p_announce->i_hash == i_hash
796 && !memcmp(p_announce->i_source, i_source, sizeof(i_source)) ) )
798 /* We don't support delete announcement as they can easily
799 * Be used to highjack an announcement by a third party.
800 * Instead we cleverly implement Implicit Announcement removal.
802 * if( b_need_delete )
803 * RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
804 * else
807 if( !b_need_delete )
809 /* No need to go after six, as we start to trust the
810 * average period at six */
811 if( p_announce->i_period_trust <= 5 )
812 p_announce->i_period_trust++;
814 /* Compute the average period */
815 mtime_t now = mdate();
816 p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust;
817 p_announce->i_last = now;
819 FreeSDP( p_sdp );
820 free (decomp);
821 return VLC_SUCCESS;
825 CreateAnnounce( p_sd, i_source, i_hash, p_sdp );
827 free (decomp);
828 return VLC_SUCCESS;
829 error:
830 free (decomp);
831 return VLC_EGENERIC;
834 sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint32_t *i_source, uint16_t i_hash,
835 sdp_t *p_sdp )
837 input_item_t *p_input;
838 const char *psz_value;
839 sap_announce_t *p_sap = (sap_announce_t *)malloc(
840 sizeof(sap_announce_t ) );
841 services_discovery_sys_t *p_sys;
842 if( p_sap == NULL )
843 return NULL;
845 p_sys = p_sd->p_sys;
847 p_sap->i_last = mdate();
848 p_sap->i_period = 0;
849 p_sap->i_period_trust = 0;
850 p_sap->i_hash = i_hash;
851 memcpy (p_sap->i_source, i_source, sizeof(p_sap->i_source));
852 p_sap->p_sdp = p_sdp;
854 /* Released in RemoveAnnounce */
855 p_input = input_item_NewStream( p_sap->p_sdp->psz_uri, p_sdp->psz_sessionname,
856 -1 );
857 if( unlikely(p_input == NULL) )
859 free( p_sap );
860 return NULL;
862 p_sap->p_item = p_input;
864 vlc_meta_t *p_meta = vlc_meta_New();
865 if( likely(p_meta != NULL) )
867 vlc_meta_Set( p_meta, vlc_meta_Description, p_sdp->psz_sessioninfo );
868 p_input->p_meta = p_meta;
871 if( p_sdp->rtcp_port )
873 char *rtcp;
874 if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 )
876 input_item_AddOption( p_input, rtcp, VLC_INPUT_OPTION_TRUSTED );
877 free( rtcp );
881 psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "tool" );
882 if( psz_value != NULL )
884 input_item_AddInfo( p_input, _("Session"), _("Tool"), "%s", psz_value );
886 if( strcmp( p_sdp->username, "-" ) )
888 input_item_AddInfo( p_input, _("Session"), _("User"), "%s",
889 p_sdp->username );
892 /* Handle category */
893 psz_value = GetAttribute(p_sap->p_sdp->pp_attributes,
894 p_sap->p_sdp->i_attributes, "cat");
895 if (psz_value != NULL)
897 /* a=cat provides a dot-separated hierarchy.
898 * For the time being only replace dots with pipe. TODO: FIXME */
899 char *str = strdup(psz_value);
900 if (likely(str != NULL))
901 for (char *p = strchr(str, '.'); p != NULL; p = strchr(p, '.'))
902 *(p++) = '|';
903 services_discovery_AddItemCat(p_sd, p_input, str ? str : psz_value);
904 free(str);
906 else
908 /* backward compatibility with VLC 0.7.3-2.0.0 senders */
909 psz_value = GetAttribute(p_sap->p_sdp->pp_attributes,
910 p_sap->p_sdp->i_attributes, "x-plgroup");
911 services_discovery_AddItemCat(p_sd, p_input, psz_value);
914 TAB_APPEND( p_sys->i_announces, p_sys->pp_announces, p_sap );
916 return p_sap;
920 static const char *FindAttribute (const sdp_t *sdp, unsigned media,
921 const char *name)
923 /* Look for media attribute, and fallback to session */
924 const char *attr = GetAttribute (sdp->mediav[media].pp_attributes,
925 sdp->mediav[media].i_attributes, name);
926 if (attr == NULL)
927 attr = GetAttribute (sdp->pp_attributes, sdp->i_attributes, name);
928 return attr;
932 /* Fill p_sdp->psz_uri */
933 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
935 if (p_sdp->mediac == 0)
937 msg_Dbg (p_obj, "Ignoring SDP with no media");
938 return VLC_EGENERIC;
941 for (unsigned i = 1; i < p_sdp->mediac; i++)
943 if ((p_sdp->mediav[i].n_addr != p_sdp->mediav->n_addr)
944 || (p_sdp->mediav[i].addrlen != p_sdp->mediav->addrlen)
945 || memcmp (&p_sdp->mediav[i].addr, &p_sdp->mediav->addr,
946 p_sdp->mediav->addrlen))
948 msg_Dbg (p_obj, "Multiple media ports not supported -> live555");
949 return VLC_EGENERIC;
953 if (p_sdp->mediav->n_addr != 1)
955 msg_Dbg (p_obj, "Layered encoding not supported -> live555");
956 return VLC_EGENERIC;
959 char psz_uri[1026];
960 const char *host;
961 int port;
963 psz_uri[0] = '[';
964 if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav->addr),
965 p_sdp->mediav->addrlen, psz_uri + 1,
966 sizeof (psz_uri) - 2, &port, NI_NUMERICHOST))
967 return VLC_EGENERIC;
969 if (strchr (psz_uri + 1, ':'))
971 host = psz_uri;
972 strcat (psz_uri, "]");
974 else
975 host = psz_uri + 1;
977 /* Parse m= field */
978 char *sdp_proto = strdup (p_sdp->mediav[0].fmt);
979 if (sdp_proto == NULL)
980 return VLC_ENOMEM;
982 char *subtype = strchr (sdp_proto, ' ');
983 if (subtype == NULL)
985 msg_Dbg (p_obj, "missing SDP media subtype: %s", sdp_proto);
986 free (sdp_proto);
987 return VLC_EGENERIC;
989 else
991 *subtype++ = '\0';
992 /* FIXME: check for multiple payload types in RTP/AVP case.
993 * FIXME: check for "mpeg" subtype in raw udp case. */
994 if (!strcasecmp (sdp_proto, "udp"))
995 p_sdp->i_media_type = 33;
996 else
997 p_sdp->i_media_type = atoi (subtype);
1000 /* RTP protocol, nul, VLC shortcut, nul, flags byte as follow:
1001 * 0x1: Connection-Oriented media. */
1002 static const char proto_match[] =
1003 "udp\0" "udp\0\0"
1004 "RTP/AVP\0" "rtp\0\0"
1005 "UDPLite/RTP/AVP\0" "udplite\0\0"
1006 "DCCP/RTP/AVP\0" "dccp\0\1"
1007 "TCP/RTP/AVP\0" "rtptcp\0\1"
1008 "\0";
1010 const char *vlc_proto = NULL;
1011 uint8_t flags = 0;
1012 for (const char *proto = proto_match; *proto;)
1014 if (strcasecmp (proto, sdp_proto) == 0)
1016 vlc_proto = proto + strlen (proto) + 1;
1017 flags = vlc_proto[strlen (vlc_proto) + 1];
1018 break;
1020 proto += strlen (proto) + 1;
1021 proto += strlen (proto) + 2;
1024 free (sdp_proto);
1025 if (vlc_proto == NULL)
1027 msg_Dbg (p_obj, "unknown SDP media protocol: %s",
1028 p_sdp->mediav[0].fmt);
1029 return VLC_EGENERIC;
1032 if (!strcmp (vlc_proto, "udp") || FindAttribute (p_sdp, 0, "rtcp-mux"))
1033 p_sdp->rtcp_port = 0;
1034 else
1036 const char *rtcp = FindAttribute (p_sdp, 0, "rtcp");
1037 if (rtcp)
1038 p_sdp->rtcp_port = atoi (rtcp);
1039 else
1040 if (port & 1) /* odd port -> RTCP; next even port -> RTP */
1041 p_sdp->rtcp_port = port++;
1042 else /* even port -> RTP; next odd port -> RTCP */
1043 p_sdp->rtcp_port = port + 1;
1046 if (flags & 1)
1048 /* Connection-oriented media */
1049 const char *setup = FindAttribute (p_sdp, 0, "setup");
1050 if (setup == NULL)
1051 setup = "active"; /* default value */
1053 if (strcmp (setup, "actpass") && strcmp (setup, "passive"))
1055 msg_Dbg (p_obj, "unsupported COMEDIA mode: %s", setup);
1056 return VLC_EGENERIC;
1059 if (asprintf (&p_sdp->psz_uri, "%s://%s:%d", vlc_proto,
1060 host, port) == -1)
1061 return VLC_ENOMEM;
1063 else
1065 /* Non-connected (normally multicast) media */
1066 char psz_source[258] = "";
1067 const char *sfilter = FindAttribute (p_sdp, 0, "source-filter");
1068 if (sfilter != NULL)
1070 char psz_source_ip[256];
1071 unsigned ipv;
1073 if (sscanf (sfilter, " incl IN IP%u %*s %255s ", &ipv,
1074 psz_source_ip) == 2)
1076 /* According to RFC4570, FQDNs can be used for source-filters,
1077 * but -seriously- this is impractical */
1078 switch (ipv)
1080 #ifdef AF_INET6
1081 case 6:
1083 struct in6_addr addr;
1084 if ((inet_pton (AF_INET6, psz_source_ip, &addr) > 0)
1085 && (inet_ntop (AF_INET6, &addr, psz_source + 1,
1086 sizeof (psz_source) - 2) != NULL))
1088 psz_source[0] = '[';
1089 psz_source[strlen (psz_source)] = ']';
1091 break;
1093 #endif
1094 case 4:
1096 struct in_addr addr;
1097 if ((inet_pton (AF_INET, psz_source_ip, &addr) > 0)
1098 && (inet_ntop (AF_INET, &addr, psz_source,
1099 sizeof (psz_source)) == NULL))
1100 *psz_source = '\0';
1101 break;
1107 if (asprintf (&p_sdp->psz_uri, "%s://%s@%s:%i", vlc_proto, psz_source,
1108 host, port) == -1)
1109 return VLC_ENOMEM;
1112 return VLC_SUCCESS;
1116 static int ParseSDPConnection (const char *str, struct sockaddr_storage *addr,
1117 socklen_t *addrlen, unsigned *number)
1119 char host[60];
1120 unsigned fam, n1, n2;
1122 int res = sscanf (str, "IN IP%u %59[^/]/%u/%u", &fam, host, &n1, &n2);
1123 if (res < 2)
1124 return -1;
1126 switch (fam)
1128 #ifdef AF_INET6
1129 case 6:
1130 addr->ss_family = AF_INET6;
1131 # ifdef HAVE_SA_LEN
1132 addr->ss_len =
1133 # endif
1134 *addrlen = sizeof (struct sockaddr_in6);
1136 if (inet_pton (AF_INET6, host,
1137 &((struct sockaddr_in6 *)addr)->sin6_addr) <= 0)
1138 return -1;
1140 *number = (res >= 3) ? n1 : 1;
1141 break;
1142 #endif
1144 case 4:
1145 addr->ss_family = AF_INET;
1146 # ifdef HAVE_SA_LEN
1147 addr->ss_len =
1148 # endif
1149 *addrlen = sizeof (struct sockaddr_in);
1151 if (inet_pton (AF_INET, host,
1152 &((struct sockaddr_in *)addr)->sin_addr) <= 0)
1153 return -1;
1155 *number = (res >= 4) ? n2 : 1;
1156 break;
1158 default:
1159 return -1;
1161 return 0;
1165 /***********************************************************************
1166 * ParseSDP : SDP parsing
1167 * *********************************************************************
1168 * Validate SDP and parse all fields
1169 ***********************************************************************/
1170 static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp)
1172 if( psz_sdp == NULL )
1173 return NULL;
1175 sdp_t *p_sdp = calloc (1, sizeof (*p_sdp));
1176 if (p_sdp == NULL)
1177 return NULL;
1179 char expect = 'V';
1180 struct sockaddr_storage glob_addr;
1181 memset (&glob_addr, 0, sizeof (glob_addr));
1182 socklen_t glob_len = 0;
1183 unsigned glob_count = 1;
1184 int port = 0;
1186 /* TODO: use iconv and charset attribute instead of EnsureUTF8 */
1187 while (*psz_sdp)
1189 /* Extract one line */
1190 size_t linelen = strcspn(psz_sdp, "\n");
1191 if (psz_sdp[linelen] == '\0')
1192 goto error;
1194 char line[linelen + 1];
1195 memcpy (line, psz_sdp, linelen);
1196 line[linelen] = '\0';
1198 psz_sdp += linelen + 1;
1200 /* Remove carriage return if present */
1201 char *eol = strchr (line, '\r');
1202 if (eol != NULL)
1204 linelen = eol - line;
1205 line[linelen] = '\0';
1208 /* Validate line */
1209 char cat = line[0], *data = line + 2;
1210 if (!cat || (strchr ("vosiuepcbtrzkam", cat) == NULL))
1212 /* MUST ignore SDP with unknown line type */
1213 msg_Dbg (p_obj, "unknown SDP line type: 0x%02x", (int)cat);
1214 goto error;
1216 if (line[1] != '=')
1218 msg_Dbg (p_obj, "invalid SDP line: %s", line);
1219 goto error;
1222 assert (linelen >= 2);
1224 /* SDP parsing state machine
1225 * We INTERNALLY use uppercase for session, lowercase for media
1227 switch (expect)
1229 /* Session description */
1230 case 'V':
1231 expect = 'O';
1232 if (cat != 'v')
1234 msg_Dbg (p_obj, "missing SDP version");
1235 goto error;
1237 if (strcmp (data, "0"))
1239 msg_Dbg (p_obj, "unknown SDP version: %s", data);
1240 goto error;
1242 break;
1244 case 'O':
1245 expect = 'S';
1246 if (cat != 'o')
1248 msg_Dbg (p_obj, "missing SDP originator");
1249 goto error;
1252 if ((sscanf (data, "%63s %"SCNu64" %"SCNu64" IN IP%u %1023s",
1253 p_sdp->username, &p_sdp->session_id,
1254 &p_sdp->session_version, &p_sdp->orig_ip_version,
1255 p_sdp->orig_host) != 5)
1256 || ((p_sdp->orig_ip_version != 4)
1257 && (p_sdp->orig_ip_version != 6)))
1259 msg_Dbg (p_obj, "SDP origin not supported: %s", data);
1260 /* Or maybe out-of-range, but this looks suspicious */
1261 goto error;
1263 EnsureUTF8 (p_sdp->orig_host);
1264 break;
1266 case 'S':
1267 expect = 'I';
1268 if ((cat != 's') || !*data)
1270 /* MUST be present AND non-empty */
1271 msg_Dbg (p_obj, "missing SDP session name");
1272 goto error;
1274 assert (p_sdp->psz_sessionname == NULL); // no memleak here
1275 p_sdp->psz_sessionname = strdup (data);
1276 if (p_sdp->psz_sessionname == NULL)
1277 goto error;
1278 EnsureUTF8 (p_sdp->psz_sessionname);
1279 break;
1281 case 'I':
1282 expect = 'U';
1283 /* optional (and may be empty) */
1284 if (cat == 'i')
1286 assert (p_sdp->psz_sessioninfo == NULL);
1287 p_sdp->psz_sessioninfo = strdup (data);
1288 if (p_sdp->psz_sessioninfo == NULL)
1289 goto error;
1290 EnsureUTF8 (p_sdp->psz_sessioninfo);
1291 break;
1293 /* fall through */
1295 case 'U':
1296 expect = 'E';
1297 if (cat == 'u')
1298 break;
1299 /* fall through */
1300 case 'E':
1301 expect = 'E';
1302 if (cat == 'e')
1303 break;
1304 /* fall through */
1305 case 'P':
1306 expect = 'P';
1307 if (cat == 'p')
1308 break;
1309 /* fall through */
1310 case 'C':
1311 expect = 'B';
1312 if (cat == 'c')
1314 if (ParseSDPConnection (data, &glob_addr, &glob_len,
1315 &glob_count))
1317 msg_Dbg (p_obj, "SDP connection infos not supported: "
1318 "%s", data);
1319 goto error;
1321 break;
1323 /* fall through */
1324 case 'B':
1325 assert (expect == 'B');
1326 if (cat == 'b')
1327 break;
1328 /* fall through */
1329 case 'T':
1330 expect = 'R';
1331 if (cat != 't')
1333 msg_Dbg (p_obj, "missing SDP time description");
1334 goto error;
1336 break;
1338 case 'R':
1339 if ((cat == 't') || (cat == 'r'))
1340 break;
1341 /* fall through */
1342 case 'Z':
1343 expect = 'K';
1344 if (cat == 'z')
1345 break;
1346 /* fall through */
1347 case 'K':
1348 expect = 'A';
1349 if (cat == 'k')
1350 break;
1351 /* fall through */
1352 case 'A':
1353 //expect = 'A';
1354 if (cat == 'a')
1356 attribute_t *p_attr = MakeAttribute (data);
1357 TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
1358 break;
1360 /* fall through */
1362 /* Media description */
1363 case 'm':
1364 media:
1366 expect = 'i';
1367 if (cat != 'm')
1369 msg_Dbg (p_obj, "missing SDP media description");
1370 goto error;
1372 struct sdp_media_t *m;
1373 m = realloc (p_sdp->mediav, (p_sdp->mediac + 1) * sizeof (*m));
1374 if (m == NULL)
1375 goto error;
1377 p_sdp->mediav = m;
1378 m += p_sdp->mediac;
1379 p_sdp->mediac++;
1381 memset (m, 0, sizeof (*m));
1382 memcpy (&m->addr, &glob_addr, m->addrlen = glob_len);
1383 m->n_addr = glob_count;
1385 /* TODO: remember media type (if we need multiple medias) */
1386 data = strchr (data, ' ');
1387 if (data == NULL)
1389 msg_Dbg (p_obj, "missing SDP media port");
1390 goto error;
1392 port = atoi (++data);
1393 if (port <= 0 || port >= 65536)
1395 msg_Dbg (p_obj, "invalid transport port %d", port);
1396 goto error;
1398 net_SetPort ((struct sockaddr *)&m->addr, htons (port));
1400 data = strchr (data, ' ');
1401 if (data == NULL)
1403 msg_Dbg (p_obj, "missing SDP media format");
1404 goto error;
1406 m->fmt = strdup (++data);
1407 if (m->fmt == NULL)
1408 goto error;
1410 break;
1413 case 'i':
1414 expect = 'c';
1415 if (cat == 'i')
1416 break;
1417 /* fall through */
1418 case 'c':
1419 expect = 'b';
1420 if (cat == 'c')
1422 struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac - 1;
1423 if (ParseSDPConnection (data, &m->addr, &m->addrlen,
1424 &m->n_addr))
1426 msg_Dbg (p_obj, "SDP connection infos not supported: "
1427 "%s", data);
1428 goto error;
1430 net_SetPort ((struct sockaddr *)&m->addr, htons (port));
1431 break;
1433 /* fall through */
1434 case 'b':
1435 expect = 'b';
1436 if (cat == 'b')
1437 break;
1438 /* fall through */
1439 case 'k':
1440 expect = 'a';
1441 if (cat == 'k')
1442 break;
1443 /* fall through */
1444 case 'a':
1445 assert (expect == 'a');
1446 if (cat == 'a')
1448 attribute_t *p_attr = MakeAttribute (data);
1449 if (p_attr == NULL)
1450 goto error;
1452 TAB_APPEND (p_sdp->mediav[p_sdp->mediac - 1].i_attributes,
1453 p_sdp->mediav[p_sdp->mediac - 1].pp_attributes, p_attr);
1454 break;
1457 if (cat == 'm')
1458 goto media;
1460 msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat);
1461 goto error;
1463 default:
1464 msg_Err (p_obj, "*** BUG in SDP parser! ***");
1465 goto error;
1469 return p_sdp;
1471 error:
1472 FreeSDP (p_sdp);
1473 return NULL;
1476 static int InitSocket( services_discovery_t *p_sd, const char *psz_address,
1477 int i_port )
1479 int i_fd = net_ListenUDP1 ((vlc_object_t *)p_sd, psz_address, i_port);
1480 if (i_fd == -1)
1481 return VLC_EGENERIC;
1483 shutdown( i_fd, SHUT_WR );
1484 TAB_APPEND(p_sd->p_sys->i_fd, p_sd->p_sys->pi_fd, i_fd);
1485 return VLC_SUCCESS;
1488 static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len )
1490 #ifdef HAVE_ZLIB_H
1491 int i_result, i_dstsize, n = 0;
1492 unsigned char *psz_dst = NULL;
1493 z_stream d_stream;
1495 memset (&d_stream, 0, sizeof (d_stream));
1497 i_result = inflateInit(&d_stream);
1498 if( i_result != Z_OK )
1499 return( -1 );
1501 d_stream.next_in = (Bytef *)psz_src;
1502 d_stream.avail_in = i_len;
1506 n++;
1507 psz_dst = xrealloc( psz_dst, n * 1000 );
1508 d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
1509 d_stream.avail_out = 1000;
1511 i_result = inflate(&d_stream, Z_NO_FLUSH);
1512 if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
1514 inflateEnd( &d_stream );
1515 free( psz_dst );
1516 return( -1 );
1519 while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
1520 ( i_result != Z_STREAM_END ) );
1522 i_dstsize = d_stream.total_out;
1523 inflateEnd( &d_stream );
1525 *_dst = xrealloc( psz_dst, i_dstsize );
1527 return i_dstsize;
1528 #else
1529 (void)psz_src;
1530 (void)_dst;
1531 (void)i_len;
1532 return -1;
1533 #endif
1537 static void FreeSDP( sdp_t *p_sdp )
1539 free( p_sdp->psz_sessionname );
1540 free( p_sdp->psz_sessioninfo );
1541 free( p_sdp->psz_uri );
1543 for (unsigned j = 0; j < p_sdp->mediac; j++)
1545 free (p_sdp->mediav[j].fmt);
1546 for (int i = 0; i < p_sdp->mediav[j].i_attributes; i++)
1547 FreeAttribute (p_sdp->mediav[j].pp_attributes[i]);
1548 free (p_sdp->mediav[j].pp_attributes);
1550 free (p_sdp->mediav);
1552 for (int i = 0; i < p_sdp->i_attributes; i++)
1553 FreeAttribute (p_sdp->pp_attributes[i]);
1555 free (p_sdp->pp_attributes);
1556 free (p_sdp);
1559 static int RemoveAnnounce( services_discovery_t *p_sd,
1560 sap_announce_t *p_announce )
1562 if( p_announce->p_sdp )
1564 FreeSDP( p_announce->p_sdp );
1565 p_announce->p_sdp = NULL;
1568 if( p_announce->p_item )
1570 services_discovery_RemoveItem( p_sd, p_announce->p_item );
1571 input_item_Release( p_announce->p_item );
1572 p_announce->p_item = NULL;
1575 TAB_REMOVE(p_sd->p_sys->i_announces, p_sd->p_sys->pp_announces,
1576 p_announce);
1577 free( p_announce );
1579 return VLC_SUCCESS;
1583 * Compare two sessions, when hash is not set (SAP v0)
1585 static bool IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 )
1587 /* A session is identified by
1588 * - username,
1589 * - session_id,
1590 * - network type (which is always IN),
1591 * - address type (currently, this means IP version),
1592 * - and hostname.
1594 if (strcmp (p_sdp1->username, p_sdp2->username)
1595 || (p_sdp1->session_id != p_sdp2->session_id)
1596 || (p_sdp1->orig_ip_version != p_sdp2->orig_ip_version)
1597 || strcmp (p_sdp1->orig_host, p_sdp2->orig_host))
1598 return false;
1600 return true;
1603 static inline attribute_t *MakeAttribute (const char *str)
1605 attribute_t *a = malloc (sizeof (*a) + strlen (str) + 1);
1606 if (a == NULL)
1607 return NULL;
1609 strcpy (a->name, str);
1610 EnsureUTF8 (a->name);
1611 char *value = strchr (a->name, ':');
1612 if (value != NULL)
1614 *value++ = '\0';
1615 a->value = value;
1617 else
1618 a->value = "";
1619 return a;
1623 static const char *GetAttribute (attribute_t **tab, unsigned n,
1624 const char *name)
1626 for (unsigned i = 0; i < n; i++)
1627 if (strcasecmp (tab[i]->name, name) == 0)
1628 return tab[i]->value;
1629 return NULL;
1633 static inline void FreeAttribute (attribute_t *a)
1635 free (a);