X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fservices_discovery%2Fsap.c;h=4b06b1f31542898a1ce1f4a95d3ad97f30a5f7df;hb=4089c841ccaade63169fbb723a4107eddafc31b0;hp=d0d1144e99bf74ba613769876a6b7fa40e6cf20f;hpb=d7a21b12e9dc5e883e2401faa817fdf211372626;p=vlc diff --git a/modules/services_discovery/sap.c b/modules/services_discovery/sap.c index d0d1144e99..4b06b1f315 100644 --- a/modules/services_discovery/sap.c +++ b/modules/services_discovery/sap.c @@ -30,6 +30,7 @@ # include "config.h" #endif +#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS #include #include #include @@ -40,14 +41,10 @@ #include #include -#include #include - -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_TIME_H -# include +#include +#ifdef HAVE_ARPA_INET_H +# include #endif #ifdef HAVE_POLL # include @@ -57,7 +54,7 @@ # include #endif -#ifndef WIN32 +#ifndef _WIN32 # include #endif @@ -86,15 +83,6 @@ #define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \ "right addresses to listen to. However, you " \ "can specify a specific address." ) -#define SAP_IPV4_TEXT N_( "IPv4 SAP" ) -#define SAP_IPV4_LONGTEXT N_( \ - "Listen to IPv4 announcements on the standard addresses." ) -#define SAP_IPV6_TEXT N_( "IPv6 SAP" ) -#define SAP_IPV6_LONGTEXT N_( \ - "Listen to IPv6 announcements on the standard addresses." ) -#define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" ) -#define SAP_SCOPE_LONGTEXT N_( \ - "Scope for IPv6 announcements (default is 8)." ) #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" ) #define SAP_TIMEOUT_LONGTEXT N_( \ "Delay after which SAP items get deleted if no new announcement " \ @@ -108,14 +96,6 @@ #define SAP_STRICT_LONGTEXT N_( \ "When this is set, the SAP parser will discard some non-compliant " \ "announcements." ) -#define SAP_CACHE_TEXT N_("Use SAP cache") -#define SAP_CACHE_LONGTEXT N_( \ - "This enables a SAP caching mechanism. " \ - "This will result in lower SAP startup time, but you could end up " \ - "with items corresponding to legacy streams." ) -#define SAP_TIMESHIFT_TEXT N_("Allow timeshifting") -#define SAP_TIMESHIFT_LONGTEXT N_( "This automatically enables timeshifting " \ - "for streams discovered through SAP announcements." ) /* Callbacks */ static int Open ( vlc_object_t * ); @@ -123,40 +103,37 @@ static int OpenDemux ( vlc_object_t * ); static void CloseDemux ( vlc_object_t * ); -vlc_module_begin(); - set_shortname( N_("SAP")); - set_description( N_("SAP Announcements") ); - set_category( CAT_PLAYLIST ); - set_subcategory( SUBCAT_PLAYLIST_SD ); - - add_string( "sap-addr", NULL, NULL, - SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, true ); - add_bool( "sap-ipv4", 1 , NULL, - SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, true ); - add_bool( "sap-ipv6", 1 , NULL, - SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, true ); - add_integer( "sap-timeout", 1800, NULL, - SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, true ); - add_bool( "sap-parse", 1 , NULL, - SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, true ); - add_bool( "sap-strict", 0 , NULL, - SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, true ); -#if 0 - add_bool( "sap-cache", 0 , NULL, - SAP_CACHE_TEXT,SAP_CACHE_LONGTEXT, true ); -#endif - add_bool( "sap-timeshift", 0 , NULL, - SAP_TIMESHIFT_TEXT,SAP_TIMESHIFT_LONGTEXT, true ); +VLC_SD_PROBE_HELPER("sap", "Network streams (SAP)", SD_CAT_LAN) + +vlc_module_begin () + set_shortname( N_("SAP")) + set_description( N_("Network streams (SAP)") ) + set_category( CAT_PLAYLIST ) + set_subcategory( SUBCAT_PLAYLIST_SD ) - set_capability( "services_discovery", 0 ); - set_callbacks( Open, Close ); + add_string( "sap-addr", NULL, + SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, true ) + add_obsolete_bool( "sap-ipv4" ) /* since 2.0.0 */ + add_obsolete_bool( "sap-ipv6" ) /* since 2.0.0 */ + add_integer( "sap-timeout", 1800, + SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, true ) + add_bool( "sap-parse", true, + SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, true ) + add_bool( "sap-strict", false, + SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, true ) + add_obsolete_bool( "sap-timeshift" ) /* Redumdant since 1.0.0 */ - add_submodule(); - set_description( N_("SDP Descriptions parser") ); - add_shortcut( "sdp" ); - set_capability( "demux", 51 ); - set_callbacks( OpenDemux, CloseDemux ); -vlc_module_end(); + set_capability( "services_discovery", 0 ) + set_callbacks( Open, Close ) + + VLC_SD_PROBE_SUBMODULE + + add_submodule () + set_description( N_("SDP Descriptions parser") ) + add_shortcut( "sdp" ) + set_capability( "demux", 51 ) + set_callbacks( OpenDemux, CloseDemux ) +vlc_module_end () /***************************************************************************** @@ -195,10 +172,14 @@ struct sdp_t /* s= field */ char *psz_sessionname; + /* i= field */ + char *psz_sessioninfo; + /* old cruft */ /* "computed" URI */ char *psz_uri; int i_media_type; + unsigned rtcp_port; /* a= global attributes */ int i_attributes; @@ -212,7 +193,7 @@ struct sdp_t struct attribute_t { const char *value; - char name[0]; + char name[]; }; struct sap_announce_t @@ -232,6 +213,8 @@ struct sap_announce_t struct services_discovery_sys_t { + vlc_thread_t thread; + /* Socket descriptors */ int i_fd; int *pi_fd; @@ -243,7 +226,6 @@ struct services_discovery_sys_t /* Modes */ bool b_strict; bool b_parse; - bool b_timeshift; int i_timeout; }; @@ -261,13 +243,13 @@ struct demux_sys_t /* Main functions */ static int Demux( demux_t *p_demux ); static int Control( demux_t *, int, va_list ); - static void Run ( services_discovery_t *p_sd ); + static void *Run ( void *p_sd ); /* Main parsing functions */ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ); static int ParseSAP( services_discovery_t *p_sd, const uint8_t *p_buffer, size_t i_read ); static sdp_t *ParseSDP (vlc_object_t *p_sd, const char *psz_sdp); - static sap_announce_t *CreateAnnounce( services_discovery_t *, uint16_t, sdp_t * ); + static sap_announce_t *CreateAnnounce( services_discovery_t *, uint32_t *, uint16_t, sdp_t * ); static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce ); /* Helper functions */ @@ -287,6 +269,24 @@ static inline int min_int( int a, int b ) return a > b ? b : a; } +static bool IsWellKnownPayload (int type) +{ + switch (type) + { /* Should be in sync with modules/demux/rtp.c */ + case 0: /* PCMU/8000 */ + case 3: + case 8: /* PCMA/8000 */ + case 10: /* L16/44100/2 */ + case 11: /* L16/44100 */ + case 12: + case 14: /* MPA/90000 */ + case 32: /* MPV/90000 */ + case 33: /* MP2/90000 */ + return true; + } + return false; +} + /***************************************************************************** * Open: initialize and create stuff *****************************************************************************/ @@ -300,30 +300,22 @@ static int Open( vlc_object_t *p_this ) p_sys->i_timeout = var_CreateGetInteger( p_sd, "sap-timeout" ); - p_sd->pf_run = Run; p_sd->p_sys = p_sys; p_sys->pi_fd = NULL; p_sys->i_fd = 0; - p_sys->b_strict = var_CreateGetInteger( p_sd, "sap-strict"); - p_sys->b_parse = var_CreateGetInteger( p_sd, "sap-parse" ); - -#if 0 - if( var_CreateGetInteger( p_sd, "sap-cache" ) ) - { - CacheLoad( p_sd ); - } -#endif - - /* Cache sap_timeshift value */ - p_sys->b_timeshift = var_CreateGetInteger( p_sd, "sap-timeshift" ); - - /* Set our name */ - services_discovery_SetLocalizedName( p_sd, _("SAP") ); + p_sys->b_strict = var_CreateGetBool( p_sd, "sap-strict"); + p_sys->b_parse = var_CreateGetBool( p_sd, "sap-parse" ); p_sys->i_announces = 0; p_sys->pp_announces = NULL; + /* TODO: create sockets here, and fix racy sockets table */ + if (vlc_clone (&p_sys->thread, Run, p_sd, VLC_THREAD_PRIORITY_LOW)) + { + free (p_sys); + return VLC_EGENERIC; + } return VLC_SUCCESS; } @@ -340,7 +332,7 @@ static int OpenDemux( vlc_object_t *p_this ) int errval = VLC_EGENERIC; size_t i_len; - if( !var_CreateGetInteger( p_demux, "sap-parse" ) ) + if( !var_CreateGetBool( p_demux, "sap-parse" ) ) { /* We want livedotcom module to parse this SDP file */ return VLC_EGENERIC; @@ -359,7 +351,7 @@ static int OpenDemux( vlc_object_t *p_this ) for( i_len = 0, psz_sdp = NULL; i_len < 65536; ) { const int i_read_max = 1024; - char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max ); + char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max + 1 ); size_t i_read; if( psz_sdp_new == NULL ) { @@ -394,22 +386,13 @@ static int OpenDemux( vlc_object_t *p_this ) { p_sdp->psz_uri = NULL; } - switch (p_sdp->i_media_type) - { /* Should be in sync with modules/demux/rtp.c */ - case 0: /* PCMU/8000 */ - case 8: /* PCMA/8000 */ - case 10: /* L16/44100/2 */ - case 11: /* L16/44100 */ - case 14: /* MPA/90000 */ - case 32: /* MPV/90000 */ - case 33: /* MP2/90000 */ - break; - default: - goto error; - } + if (!IsWellKnownPayload (p_sdp->i_media_type)) + goto error; if( p_sdp->psz_uri == NULL ) goto error; p_demux->p_sys = (demux_sys_t *)malloc( sizeof(demux_sys_t) ); + if( unlikely( !p_demux->p_sys ) ) + goto error; p_demux->p_sys->p_sdp = p_sdp; p_demux->pf_control = Control; p_demux->pf_demux = Demux; @@ -431,22 +414,17 @@ static void Close( vlc_object_t *p_this ) { services_discovery_t *p_sd = ( services_discovery_t* )p_this; services_discovery_sys_t *p_sys = p_sd->p_sys; - int i; + vlc_cancel (p_sys->thread); + vlc_join (p_sys->thread, NULL); + for( i = p_sys->i_fd-1 ; i >= 0 ; i-- ) { net_Close( p_sys->pi_fd[i] ); } FREENULL( p_sys->pi_fd ); -#if 0 - if( config_GetInt( p_sd, "sap-cache" ) ) - { - CacheSave( p_sd ); - } -#endif - for( i = p_sys->i_announces - 1; i>= 0; i-- ) { RemoveAnnounce( p_sd, p_sys->pp_announces[i] ); @@ -462,11 +440,10 @@ static void Close( vlc_object_t *p_this ) static void CloseDemux( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t *)p_this; - if( p_demux->p_sys ) - { - if( p_demux->p_sys->p_sdp ) { FreeSDP( p_demux->p_sys->p_sdp ); p_demux->p_sys->p_sdp = NULL; } - free( p_demux->p_sys ); - } + + if( p_demux->p_sys->p_sdp ) + FreeSDP( p_demux->p_sys->p_sdp ); + free( p_demux->p_sys ); } /***************************************************************************** @@ -476,11 +453,13 @@ static void CloseDemux( vlc_object_t *p_this ) *****************************************************************************/ #define MAX_SAP_BUFFER 5000 -static void Run( services_discovery_t *p_sd ) +static void *Run( void *data ) { + services_discovery_t *p_sd = data; char *psz_addr; int i; int timeout = -1; + int canc = vlc_savecancel (); /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed * DNS queries, even if the DNS server returns an error with milliseconds. @@ -488,29 +467,24 @@ static void Run( services_discovery_t *p_sd ) * Winsock 1.1 from Windows 95, if not Windows 3.1. * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation, * we have to open sockets in Run() rather than Open(). */ - if( var_CreateGetInteger( p_sd, "sap-ipv4" ) ) + InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT ); + InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT ); + InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT ); + InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT ); + + char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%"; +#ifndef _WIN32 + struct if_nameindex *l = if_nameindex (); + if (l != NULL) { - InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT ); - InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT ); - InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT ); - InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT ); - } - if( var_CreateGetInteger( p_sd, "sap-ipv6" ) ) - { - char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%"; - -#ifndef WIN32 - struct if_nameindex *l = if_nameindex (); - if (l != NULL) + char *ptr = strchr (psz_address, '%') + 1; + for (unsigned i = 0; l[i].if_index; i++) { - char *ptr = strchr (psz_address, '%') + 1; - for (unsigned i = 0; l[i].if_index; i++) - { - strcpy (ptr, l[i].if_name); - InitSocket (p_sd, psz_address, SAP_PORT); - } - if_freenameindex (l); + strcpy (ptr, l[i].if_name); + InitSocket (p_sd, psz_address, SAP_PORT); } + if_freenameindex (l); + } #else /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks, which if_nameindex uses internally anyway */ @@ -520,7 +494,7 @@ static void Run( services_discovery_t *p_sd ) if( s != INVALID_SOCKET ) { INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces - size_t len = sizeof(ifaces); + DWORD len = sizeof(ifaces); if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) ) { @@ -535,14 +509,13 @@ static void Run( services_discovery_t *p_sd ) closesocket(s); } #endif - *strchr (psz_address, '%') = '\0'; + *strchr (psz_address, '%') = '\0'; - static const char ipv6_scopes[] = "1456789ABCDE"; - for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++) - { - psz_address[3] = *c_scope; - InitSocket( p_sd, psz_address, SAP_PORT ); - } + static const char ipv6_scopes[] = "1456789ABCDE"; + for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++) + { + psz_address[3] = *c_scope; + InitSocket( p_sd, psz_address, SAP_PORT ); } psz_addr = var_CreateGetString( p_sd, "sap-addr" ); @@ -553,16 +526,15 @@ static void Run( services_discovery_t *p_sd ) if( p_sd->p_sys->i_fd == 0 ) { msg_Err( p_sd, "unable to listen on any address" ); - return; + return NULL; } - vlc_object_lock( p_sd ); - /* read SAP packets */ - while( vlc_object_alive( p_sd ) ) + for (;;) { + vlc_restorecancel (canc); unsigned n = p_sd->p_sys->i_fd; - struct pollfd ufd[n+1]; + struct pollfd ufd[n]; for (unsigned i = 0; i < n; i++) { @@ -571,23 +543,9 @@ static void Run( services_discovery_t *p_sd ) ufd[i].revents = 0; } - /* Make sure we track vlc_object_signal() */ - ufd[n].fd = vlc_object_waitpipe( p_sd ); - ufd[n].events = POLLIN | POLLHUP; - ufd[n].revents = 0; - - if( ufd[n].fd == -1 ) - { - /* On windows, fd will be -1, as we can't select on a pipe()-ed - * fildes. Because we have no other solution to track that - * object is killed, we make sure the timeout won't be to long. */ - if( timeout > 1000 || timeout == -1 ) - timeout = 1000; - } - - vlc_object_unlock( p_sd ); - - if (poll (ufd, n+1, timeout) > 0) + int val = poll (ufd, n, timeout); + canc = vlc_savecancel (); + if (val > 0) { for (unsigned i = 0; i < n; i++) { @@ -599,7 +557,8 @@ static void Run( services_discovery_t *p_sd ) i_read = net_Read (p_sd, ufd[i].fd, NULL, p_buffer, MAX_SAP_BUFFER, false); if (i_read < 0) - msg_Warn (p_sd, "receive error: %m"); + msg_Warn (p_sd, "receive error: %s", + vlc_strerror_c(errno)); if (i_read > 6) { /* Parse the packet */ @@ -612,7 +571,7 @@ static void Run( services_discovery_t *p_sd ) mtime_t now = mdate(); - /* A 1 hour timeout correspong to the RFC Implicit timeout. + /* A 1 hour timeout correspond to the RFC Implicit timeout. * This timeout is tuned in the following loop. */ timeout = 1000 * 60 * 60; @@ -623,10 +582,10 @@ static void Run( services_discovery_t *p_sd ) sap_announce_t * p_announce = p_sd->p_sys->pp_announces[i]; mtime_t i_last_period = now - p_announce->i_last; - /* Remove the annoucement, if the last announcement was 1 hour ago - * or if the last packet emitted was 3 times the average time + /* Remove the announcement, if the last announcement was 1 hour ago + * or if the last packet emitted was 10 times the average time * between two packets */ - if( ( p_announce->i_period_trust > 5 && i_last_period > 3 * p_announce->i_period ) || + if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) || i_last_period > i_timeout ) { RemoveAnnounce( p_sd, p_announce ); @@ -635,19 +594,17 @@ static void Run( services_discovery_t *p_sd ) { /* Compute next timeout */ if( p_announce->i_period_trust > 5 ) - timeout = min_int((3 * p_announce->i_period - i_last_period) / 1000, timeout); + timeout = min_int((10 * p_announce->i_period - i_last_period) / 1000, timeout); timeout = min_int((i_timeout - i_last_period)/1000, timeout); } } if( !p_sd->p_sys->i_announces ) - timeout = -1; /* We can safely poll indefinitly. */ + timeout = -1; /* We can safely poll indefinitely. */ else if( timeout < 200 ) timeout = 200; /* Don't wakeup too fast. */ - - vlc_object_lock( p_sd ); } - vlc_object_unlock( p_sd ); + vlc_assert_unreachable (); } /********************************************************************** @@ -660,8 +617,7 @@ static int Demux( demux_t *p_demux ) input_thread_t *p_input; input_item_t *p_parent_input; - p_input = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, - FIND_PARENT ); + p_input = demux_GetParentInput( p_demux ); assert( p_input ); if( !p_input ) { @@ -669,18 +625,29 @@ static int Demux( demux_t *p_demux ) return VLC_EGENERIC; } - /* This item hasn't been yield by input_GetItem + /* This item hasn't been held by input_GetItem * don't release it */ p_parent_input = input_GetItem( p_input ); input_item_SetURI( p_parent_input, p_sdp->psz_uri ); input_item_SetName( p_parent_input, p_sdp->psz_sessionname ); + if( p_sdp->rtcp_port ) + { + char *rtcp; + if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 ) + { + input_item_AddOption( p_parent_input, rtcp, VLC_INPUT_OPTION_TRUSTED ); + free( rtcp ); + } + } vlc_mutex_lock( &p_parent_input->lock ); - p_parent_input->i_type = ITEM_TYPE_NET; + p_parent_input->i_type = ITEM_TYPE_STREAM; + p_parent_input->b_net = true; vlc_mutex_unlock( &p_parent_input->lock ); + vlc_object_release( p_input ); return VLC_SUCCESS; } @@ -702,6 +669,7 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, const char *psz_sdp; const uint8_t *end = buf + len; sdp_t *p_sdp; + uint32_t i_source[4]; assert (buf[len] == '\0'); @@ -709,6 +677,7 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, return VLC_EGENERIC; uint8_t flags = buf[0]; + uint8_t auth_len = buf[1]; /* First, check the sap announce is correct */ if ((flags >> 5) != 1) @@ -733,8 +702,20 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, return VLC_EGENERIC; } - // Skips source address and auth data - buf += 4 + (b_ipv6 ? 16 : 4) + buf[1]; + buf += 4; + if( b_ipv6 ) + { + for( int i = 0; i < 4; i++,buf+=4) + i_source[i] = U32_AT(buf); + } + else + { + memset(i_source, 0, sizeof(i_source)); + i_source[3] = U32_AT(buf); + buf+=4; + } + // Skips auth data + buf += auth_len; if (buf > end) return VLC_EGENERIC; @@ -771,12 +752,12 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, if (strcmp (psz_sdp, "application/sdp")) { msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp); - return VLC_EGENERIC; + goto error; } // skips content type if (len <= clen) - return VLC_EGENERIC; + goto error; len -= clen; psz_sdp += clen; @@ -786,7 +767,7 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp ); if( p_sdp == NULL ) - return VLC_EGENERIC; + goto error; p_sdp->psz_sdp = psz_sdp; @@ -796,10 +777,7 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, p_sdp->psz_uri = NULL; /* Multi-media or no-parse -> pass to LIVE.COM */ - if( ( p_sdp->i_media_type != 14 - && p_sdp->i_media_type != 32 - && p_sdp->i_media_type != 33) - || p_sd->p_sys->b_parse == false ) + if( !IsWellKnownPayload( p_sdp->i_media_type ) || !p_sd->p_sys->b_parse ) { free( p_sdp->psz_uri ); if (asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp ) == -1) @@ -809,19 +787,20 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, if( p_sdp->psz_uri == NULL ) { FreeSDP( p_sdp ); - return VLC_EGENERIC; + goto error; } for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ ) { sap_announce_t * p_announce = p_sd->p_sys->pp_announces[i]; /* FIXME: slow */ - /* FIXME: we create a new announce each time the sdp changes */ - if( IsSameSession( p_announce->p_sdp, p_sdp ) ) + if( ( !i_hash && IsSameSession( p_announce->p_sdp, p_sdp ) ) + || ( i_hash && p_announce->i_hash == i_hash + && !memcmp(p_announce->i_source, i_source, sizeof(i_source)) ) ) { /* We don't support delete announcement as they can easily * Be used to highjack an announcement by a third party. - * Intead we cleverly implement Implicit Announcement removal. + * Instead we cleverly implement Implicit Announcement removal. * * if( b_need_delete ) * RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]); @@ -836,21 +815,26 @@ static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf, p_announce->i_period_trust++; /* Compute the average period */ - p_announce->i_period = (p_announce->i_period + (mdate() - p_announce->i_last)) / 2; - p_announce->i_last = mdate(); + mtime_t now = mdate(); + p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust; + p_announce->i_last = now; } - FreeSDP( p_sdp ); p_sdp = NULL; + FreeSDP( p_sdp ); + free (decomp); return VLC_SUCCESS; } } - CreateAnnounce( p_sd, i_hash, p_sdp ); + CreateAnnounce( p_sd, i_source, i_hash, p_sdp ); - FREENULL (decomp); + free (decomp); return VLC_SUCCESS; +error: + free (decomp); + return VLC_EGENERIC; } -sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash, +sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint32_t *i_source, uint16_t i_hash, sdp_t *p_sdp ) { input_item_t *p_input; @@ -867,41 +851,69 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash, p_sap->i_period = 0; p_sap->i_period_trust = 0; p_sap->i_hash = i_hash; + memcpy (p_sap->i_source, i_source, sizeof(p_sap->i_source)); p_sap->p_sdp = p_sdp; /* Released in RemoveAnnounce */ - p_input = input_ItemNewWithType( VLC_OBJECT(p_sd), - p_sap->p_sdp->psz_uri, - p_sdp->psz_sessionname, - 0, NULL, -1, ITEM_TYPE_NET ); - p_sap->p_item = p_input; - if( !p_input ) + p_input = input_item_NewWithType( p_sap->p_sdp->psz_uri, + p_sdp->psz_sessionname, + 0, NULL, 0, -1, ITEM_TYPE_STREAM ); + if( unlikely(p_input == NULL) ) { free( p_sap ); return NULL; } + p_sap->p_item = p_input; + + vlc_meta_t *p_meta = vlc_meta_New(); + if( likely(p_meta != NULL) ) + { + vlc_meta_Set( p_meta, vlc_meta_Description, p_sdp->psz_sessioninfo ); + p_input->p_meta = p_meta; + } - if( p_sys->b_timeshift ) - input_ItemAddOption( p_input, ":access-filter=timeshift" ); + if( p_sdp->rtcp_port ) + { + char *rtcp; + if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 ) + { + input_item_AddOption( p_input, rtcp, VLC_INPUT_OPTION_TRUSTED ); + free( rtcp ); + } + } psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "tool" ); if( psz_value != NULL ) { - input_ItemAddInfo( p_input, _("Session"), _("Tool"), "%s", psz_value ); + input_item_AddInfo( p_input, _("Session"), _("Tool"), "%s", psz_value ); } if( strcmp( p_sdp->username, "-" ) ) { - input_ItemAddInfo( p_input, _("Session"), _("User"), "%s", + input_item_AddInfo( p_input, _("Session"), _("User"), "%s", p_sdp->username ); } - /* Handle group */ - if (p_sap->p_sdp->mediac >= 1) - psz_value = FindAttribute (p_sap->p_sdp, 0, "x-plgroup"); + /* Handle category */ + psz_value = GetAttribute(p_sap->p_sdp->pp_attributes, + p_sap->p_sdp->i_attributes, "cat"); + if (psz_value != NULL) + { + /* a=cat provides a dot-separated hierarchy. + * For the time being only replace dots with pipe. TODO: FIXME */ + char *str = strdup(psz_value); + if (likely(str != NULL)) + for (char *p = strchr(str, '.'); p != NULL; p = strchr(p, '.')) + *(p++) = '|'; + services_discovery_AddItem(p_sd, p_input, str ? str : psz_value); + free(str); + } else - psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "x-plgroup" ); - - services_discovery_AddItem( p_sd, p_input, psz_value /* category name */ ); + { + /* backward compatibility with VLC 0.7.3-2.0.0 senders */ + psz_value = GetAttribute(p_sap->p_sdp->pp_attributes, + p_sap->p_sdp->i_attributes, "x-plgroup"); + services_discovery_AddItem(p_sd, p_input, psz_value); + } TAB_APPEND( p_sys->i_announces, p_sys->pp_announces, p_sap ); @@ -913,9 +925,11 @@ static const char *FindAttribute (const sdp_t *sdp, unsigned media, const char *name) { /* Look for media attribute, and fallback to session */ - return GetAttribute (sdp->mediav[media].pp_attributes, - sdp->mediav[media].i_attributes, name) - ?: GetAttribute (sdp->pp_attributes, sdp->i_attributes, name); + const char *attr = GetAttribute (sdp->mediav[media].pp_attributes, + sdp->mediav[media].i_attributes, name); + if (attr == NULL) + attr = GetAttribute (sdp->pp_attributes, sdp->i_attributes, name); + return attr; } @@ -1019,6 +1033,20 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) return VLC_EGENERIC; } + if (!strcmp (vlc_proto, "udp") || FindAttribute (p_sdp, 0, "rtcp-mux")) + p_sdp->rtcp_port = 0; + else + { + const char *rtcp = FindAttribute (p_sdp, 0, "rtcp"); + if (rtcp) + p_sdp->rtcp_port = atoi (rtcp); + else + if (port & 1) /* odd port -> RTCP; next even port -> RTP */ + p_sdp->rtcp_port = port++; + else /* even port -> RTP; next odd port -> RTCP */ + p_sdp->rtcp_port = port + 1; + } + if (flags & 1) { /* Connection-oriented media */ @@ -1039,7 +1067,6 @@ static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp ) else { /* Non-connected (normally multicast) media */ - char psz_source[258] = ""; const char *sfilter = FindAttribute (p_sdp, 0, "source-filter"); if (sfilter != NULL) @@ -1225,16 +1252,16 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) goto error; } - if ((sscanf (data, "%63s %"PRIu64" %"PRIu64" IN IP%u %1023s", + if ((sscanf (data, "%63s %"SCNu64" %"SCNu64" IN IP%u %1023s", p_sdp->username, &p_sdp->session_id, &p_sdp->session_version, &p_sdp->orig_ip_version, p_sdp->orig_host) != 5) || ((p_sdp->orig_ip_version != 4) && (p_sdp->orig_ip_version != 6))) { - msg_Dbg (p_obj, "SDP origin not supported: %s\n", data); + msg_Dbg (p_obj, "SDP origin not supported: %s", data); /* Or maybe out-of-range, but this looks suspicious */ - return NULL; + goto error; } EnsureUTF8 (p_sdp->orig_host); break; @@ -1251,16 +1278,27 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) } assert (p_sdp->psz_sessionname == NULL); // no memleak here p_sdp->psz_sessionname = strdup (data); - EnsureUTF8 (p_sdp->psz_sessionname); if (p_sdp->psz_sessionname == NULL) goto error; + EnsureUTF8 (p_sdp->psz_sessionname); break; } case 'I': + { expect = 'U'; + /* optional (and may be empty) */ if (cat == 'i') + { + assert (p_sdp->psz_sessioninfo == NULL); + p_sdp->psz_sessioninfo = strdup (data); + if (p_sdp->psz_sessioninfo == NULL) + goto error; + EnsureUTF8 (p_sdp->psz_sessioninfo); break; + } + } + case 'U': expect = 'E'; if (cat == 'u') @@ -1413,12 +1451,8 @@ static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) if (cat == 'm') goto media; - if (cat != 'm') - { - msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat); - goto error; - } - break; + msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat); + goto error; default: msg_Err (p_obj, "*** BUG in SDP parser! ***"); @@ -1473,6 +1507,7 @@ static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) ) { inflateEnd( &d_stream ); + free( psz_dst ); return( -1 ); } } @@ -1497,6 +1532,7 @@ static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i static void FreeSDP( sdp_t *p_sdp ) { free( p_sdp->psz_sessionname ); + free( p_sdp->psz_sessioninfo ); free( p_sdp->psz_uri ); for (unsigned j = 0; j < p_sdp->mediac; j++) @@ -1548,6 +1584,9 @@ static int RemoveAnnounce( services_discovery_t *p_sd, return VLC_SUCCESS; } +/* + * Compare two sessions, when hash is not set (SAP v0) + */ static bool IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 ) { /* A session is identified by @@ -1566,7 +1605,6 @@ static bool IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 ) return true; } - static inline attribute_t *MakeAttribute (const char *str) { attribute_t *a = malloc (sizeof (*a) + strlen (str) + 1);