X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fstream_output%2Fannounce.c;h=7dedc1e0508c412716c6c08213d29f5bd8243f11;hb=6a66117a34f627ad9560b07cead07663b48f23b6;hp=306942b8af5c2e4d77879c049b2fb4283e6cd4af;hpb=b83deb14dfba98e2695e437b7f66c65fd8bb2713;p=vlc diff --git a/src/stream_output/announce.c b/src/stream_output/announce.c index 306942b8af..7dedc1e050 100644 --- a/src/stream_output/announce.c +++ b/src/stream_output/announce.c @@ -1,10 +1,10 @@ /***************************************************************************** - * announce.c : Session announcement + * announce.c : announce handler ***************************************************************************** - * Copyright (C) 2002 VideoLAN + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ * - * Authors: Clément Stenac - * Damien Lucas + * Authors: Clément Stenac * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,495 +18,217 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* ENOMEM */ -#include /* free() */ -#include /* sprintf() */ -#include /* strerror() */ - -#include -#include - -#ifdef HAVE_UNISTD_H -# include -#endif - -#ifdef WIN32 -# include -# include -# ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -# endif -#else -# include -#endif - -#ifdef HAVE_SLP_H -# include +#ifdef HAVE_CONFIG_H +# include "config.h" #endif -#include "announce.h" -#include "network.h" +#include +#include +#include "stream_output.h" -#define SAP_IPV4_ADDR "224.2.127.254" /* Standard port and address for SAP */ -#define SAP_PORT 9875 +#include -#define SAP_IPV6_ADDR_1 "FF0" -#define SAP_IPV6_ADDR_2 "::2:7FFE" +/* Private functions for the announce handler */ +static announce_handler_t* announce_HandlerCreate( vlc_object_t *); +static int announce_Register( announce_handler_t *p_announce, + session_descriptor_t *p_session, + announce_method_t *p_method ); +static int announce_UnRegister( announce_handler_t *p_announce, + session_descriptor_t *p_session ); -#define DEFAULT_PORT "1234" +struct announce_method_t +{ +} sap_method; /**************************************************************************** - * Split : split a string into two parts: the one which is before the delim - * and the one which is after. - * NULL is returned if delim is not found + * Sout-side functions ****************************************************************************/ -static char * split( char *psz_in, char *psz_out1, char *psz_out2, char delim) -{ - unsigned int i_count = 0; /* pos in input string */ - unsigned int i_pos1 = 0; /* pos in out2 string */ - unsigned int i_pos2 = 0; - char *psz_cur; /* store the pos of the first delim found */ - - /* Skip spaces at the beginning */ - while( psz_in[i_count] == ' ' ) - { - i_count++; - } - - if( psz_in[i_count] == '\0' ) - { - return NULL; - } - - /* Look for delim */ - while( psz_in[i_count] && psz_in[i_count] != delim ) - { - psz_out1[i_pos1] = psz_in[i_count]; - i_count++; - i_pos1++; - } - /* Mark the end of out1 */ - psz_out1[i_pos1] = '\0'; - - if( psz_in[i_count] == '\0' ) - { - return NULL; - } - - /* store pos of the first delim */ - psz_cur = psz_in + i_count; - - /* skip all delim and all spaces */ - while( psz_in[i_count] == ' ' || psz_in[i_count] == delim ) - { - i_count++; - } - - if( psz_in[i_count] == '\0' ) - { - return psz_cur; - } - - /* Store the second string */ - while( psz_in[i_count] ) - { - psz_out2[i_pos2] = psz_in[i_count]; - i_pos2++; - i_count++; - } - psz_out2[i_pos2] = '\0'; - - return psz_cur; -} - -/***************************************************************************** - * sout_SAPNew: Creates a SAP Session - *****************************************************************************/ -sap_session_t * sout_SAPNew ( sout_instance_t *p_sout, char * psz_url_arg, - char * psz_name_arg, int ip_version, - char * psz_v6_scope ) +/** + * Register a new session with the announce handler, using a pregenerated SDP + * + * \param p_sout a sout instance structure + * \param psz_sdp the SDP to register + * \param psz_dst session address (needed for SAP address auto detection) + * \param p_method an announce method descriptor + * \return the new session descriptor structure + */ +session_descriptor_t * +sout_AnnounceRegisterSDP( sout_instance_t *p_sout, const char *psz_sdp, + const char *psz_dst, announce_method_t *p_method ) { - sap_session_t *p_sap; /* The SAP structure */ - module_t *p_network; /* Network module */ - network_socket_t socket_desc; /* Socket descriptor */ - char *sap_ipv6_addr = NULL; /* IPv6 built address */ - char *psz_eol; /* Used to parse IPv6 URIs */ - int i_port; /* Port in numerical format */ - - /* Allocate the SAP structure */ - p_sap = (sap_session_t *) malloc( sizeof ( sap_session_t ) ) ; - if ( !p_sap ) - { - msg_Err( p_sout, "out of memory" ); - return NULL; - } - - /* Fill the information in the structure */ - if( strstr( psz_url_arg, "[" ) ) - { - /* We have an IPv6 address. Do not use ':' as the port separator */ - psz_eol = strchr( psz_url_arg, ']' ); - if( !psz_eol ) + session_descriptor_t *p_session; + announce_handler_t *p_announce = (announce_handler_t*) + vlc_object_find( p_sout, + VLC_OBJECT_ANNOUNCE, + FIND_ANYWHERE ); + if( !p_announce ) + { + msg_Dbg( p_sout, "no announce handler found, creating one" ); + p_announce = announce_HandlerCreate( VLC_OBJECT( p_sout ) ); + if( !p_announce ) { - msg_Warn( p_sout, "no matching ], unable to parse URI"); + msg_Err( p_sout, "Creation failed" ); return NULL; } - - if( !psz_eol++ ) - { - sprintf( p_sap->psz_url, "%s", psz_url_arg ); - sprintf( p_sap->psz_port, "%s", DEFAULT_PORT ); - } - else - { - *psz_eol = '\0'; - sprintf( p_sap->psz_url, "%s", psz_url_arg ); - psz_eol++; - if( psz_eol ) - { - sprintf( p_sap->psz_port, "%s", psz_eol ); - } - } - } - else - { - split( psz_url_arg, p_sap->psz_url, p_sap->psz_port, ':' ); + vlc_object_yield( p_announce ); } - /* Check if we have a port */ - if( !strlen( p_sap->psz_port ) ) - { - sprintf( p_sap->psz_port, "%s", DEFAULT_PORT ); - } - - /* Make sure our port is valid and atoi it */ - i_port = atoi( p_sap->psz_port ); + p_session = malloc( sizeof( *p_session ) ); + memset( p_session, 0, sizeof( *p_session ) ); + p_session->psz_sdp = strdup( psz_sdp ); - if( !i_port ) + /* GRUIK. We should not convert back-and-forth from string to numbers */ + struct addrinfo *res; + if (vlc_getaddrinfo (VLC_OBJECT (p_sout), psz_dst, 0, NULL, &res) == 0) { - sprintf( p_sap->psz_port, "%s", DEFAULT_PORT ); + if (res->ai_addrlen <= sizeof (p_session->addr)) + memcpy (&p_session->addr, res->ai_addr, + p_session->addrlen = res->ai_addrlen); + vlc_freeaddrinfo (res); } - else - { - sprintf( p_sap->psz_port, "%i", i_port ); - } - - /* The name that we send */ - sprintf( p_sap->psz_name, "%s", psz_name_arg ); - - p_sap->i_ip_version = ip_version; - - /* Only "6" triggers IPv6. IPv4 is default */ - if( ip_version != 6 ) - { - msg_Dbg( p_sout, "creating IPv4 SAP socket" ); - /* Fill the socket descriptor */ - socket_desc.i_type = NETWORK_UDP; - socket_desc.psz_bind_addr = ""; - socket_desc.i_bind_port = 0; - socket_desc.psz_server_addr = SAP_IPV4_ADDR; - socket_desc.i_server_port = SAP_PORT; - socket_desc.i_handle = 0; - socket_desc.i_ttl = 0; - - /* Call the network module */ - p_sout->p_private = (void*) &socket_desc; - if( !( p_network = module_Need( p_sout, "network", "ipv4" ) ) ) - { - msg_Warn( p_sout, "failed to open a connection (udp)" ); - return NULL; - } - module_Unneed( p_sout, p_network ); - - p_sap->i_socket = socket_desc.i_handle; - if( p_sap->i_socket < 0 ) - { - msg_Warn( p_sout, "unable to initialize SAP" ); - return NULL; - } - } - else - { - msg_Dbg( p_sout, "creating IPv6 SAP socket with scope %s", - psz_v6_scope ); - - /* Initialize and build the IPv6 address to broadcast to */ - sap_ipv6_addr = (char *) malloc( 28 * sizeof(char) ); - if ( !sap_ipv6_addr ) - { - msg_Err( p_sout, "out of memory" ); - return NULL; - } - sprintf( sap_ipv6_addr, "%s%c%s", - SAP_IPV6_ADDR_1, psz_v6_scope[0], SAP_IPV6_ADDR_2 ); + announce_Register( p_announce, p_session, p_method ); - /* Fill the socket descriptor */ - socket_desc.i_type = NETWORK_UDP; - socket_desc.psz_bind_addr = ""; - socket_desc.i_bind_port = 0; - socket_desc.psz_server_addr = sap_ipv6_addr; - socket_desc.i_server_port = SAP_PORT; - socket_desc.i_handle = 0; - socket_desc.i_ttl = 0; - - /* Call the network module */ - p_sout->p_private = (void *) &socket_desc; - if( !( p_network = module_Need( p_sout, "network", "ipv6" ) ) ) - { - msg_Warn( p_sout, "failed to open a connection (udp)" ); - return NULL; - } - module_Unneed( p_sout, p_network ); - - p_sap->i_socket = socket_desc.i_handle; - if( p_sap->i_socket <= 0 ) - { - msg_Warn( p_sout, "unable to initialize SAP" ); - return NULL; - } - - /* Free what we allocated */ - if( sap_ipv6_addr ) - { - free( sap_ipv6_addr ); - } - } - - msg_Dbg( p_sout, "SAP initialization complete" ); - - return p_sap; + vlc_object_release( p_announce ); + return p_session; } -/***************************************************************************** - * sout_SAPDelete: Deletes a SAP Session - *****************************************************************************/ -void sout_SAPDelete( sout_instance_t *p_sout, sap_session_t * p_sap ) +/** + * UnRegister an existing session + * + * \param p_sout a sout instance structure + * \param p_session the session descriptor + * \return VLC_SUCCESS or an error + */ +int sout_AnnounceUnRegister( sout_instance_t *p_sout, + session_descriptor_t *p_session ) { int i_ret; - -#if defined( UNDER_CE ) - i_ret = CloseHandle( (HANDLE)p_sap->i_socket ); -#elif defined( WIN32 ) - i_ret = closesocket( p_sap->i_socket ); -#else - i_ret = close( p_sap->i_socket ); -#endif - - if( i_ret ) + announce_handler_t *p_announce = (announce_handler_t*) + vlc_object_find( p_sout, + VLC_OBJECT_ANNOUNCE, + FIND_ANYWHERE ); + if( !p_announce ) { - msg_Err( p_sout, "unable to close SAP socket" ); + msg_Dbg( p_sout, "unable to remove announce: no announce handler" ); + return VLC_ENOOBJ; } + i_ret = announce_UnRegister( p_announce, p_session ); + if( i_ret == 0 ) + free( p_session ); - free( p_sap ); + vlc_object_release( p_announce ); + + return i_ret; } -/***************************************************************************** - * sout_SAPSend: Sends a SAP packet - *****************************************************************************/ -void sout_SAPSend( sout_instance_t *p_sout, sap_session_t * p_sap ) +/** + * \return the SAP announce method + */ +announce_method_t * sout_SAPMethod (void) { - char psz_msg[1000]; /* SDP content */ - char *psz_head; /* SAP header */ - char *psz_send; /* What we send */ - char *psz_type = "application/sdp"; - int i_header_size; /* SAP header size */ - int i_msg_size; /* SDP content size */ - int i_size; /* Total size */ - int i_ret = 0; - - /* We send a packet every 24 calls to the function */ - if( p_sap->i_calls++ < 24 ) - { - return; - } - - i_header_size = 8 + strlen( psz_type ) + 1; - psz_head = (char *) malloc( i_header_size * sizeof( char ) ); - - if( ! psz_head ) - { - msg_Err( p_sout, "out of memory" ); - return; - } - - /* Create the SAP headers */ - psz_head[0] = 0x20; /* Means IPv4, not encrypted, not compressed */ - psz_head[1] = 0x00; /* No authentification */ - psz_head[2] = 0x42; /* Version */ - psz_head[3] = 0x12; /* Version */ - - psz_head[4] = 0x01; /* Source IP FIXME: we should get the real address */ - psz_head[5] = 0x02; /* idem */ - psz_head[6] = 0x03; /* idem */ - psz_head[7] = 0x04; /* idem */ - - strncpy( psz_head + 8, psz_type, 15 ); - psz_head[ i_header_size-1 ] = '\0'; - - /* Create the SDP content */ - /* Do not add spaces at beginning of the lines ! */ - sprintf( psz_msg, "v=0\n" - "o=VideoLAN 3247692199 3247895918 IN IP4 VideoLAN\n" - "s=%s\n" - "u=VideoLAN\n" - "t=0 0\n" - "m=audio %s udp 14\n" - "c=IN IP4 %s/15\n" - "a=type:test\n", - p_sap->psz_name, p_sap->psz_port, p_sap->psz_url ); - - i_msg_size = strlen( psz_msg ); - i_size = i_msg_size + i_header_size; - - /* Create the message */ - psz_send = (char *) malloc( i_size*sizeof(char) ); - if( !psz_send ) - { - msg_Err( p_sout, "out of memory" ); - return; - } - - memcpy( psz_send, psz_head, i_header_size ); - memcpy( psz_send + i_header_size, psz_msg, i_msg_size ); - - if( i_size < 1024 ) /* We mustn't send packets larger than 1024B */ - { - i_ret = send( p_sap->i_socket, psz_send, i_size, 0 ); - } - - if( i_ret <= 0 ) - { - msg_Warn( p_sout, "SAP send failed on socket %i (%s)", - p_sap->i_socket, strerror(errno) ); - } - - p_sap->i_calls = 0; + return &sap_method; +} - /* Free what we allocated */ - free( psz_send ); - free( psz_head ); +void sout_MethodRelease (announce_method_t *m) +{ + assert (m == &sap_method); } +/************************************************************************ + * Announce handler functions (private) + ************************************************************************/ - -/***************************************************************************** - * sout_SLPBuildName: Builds a service name according to SLP standard - *****************************************************************************/ -char * sout_SLPBuildName(char *psz_url,char *psz_name) +/** + * Create the announce handler object + * + * \param p_this a vlc_object structure + * \return the new announce handler or NULL on error + */ +static announce_handler_t *announce_HandlerCreate( vlc_object_t *p_this ) { - char *psz_service; - unsigned int i_size; - - /* name to build is: service:$(name).videolan://$(url) */ + announce_handler_t *p_announce; - i_size = 8 + strlen(psz_name) + 12 + strlen(psz_url) + 1; + p_announce = vlc_object_create( p_this, VLC_OBJECT_ANNOUNCE ); - psz_service=(char *)malloc(i_size * sizeof(char)); + if( !p_announce ) + return NULL; - snprintf( psz_service , i_size, - "service:%s.videolan://%s", - psz_name,psz_url); - psz_service[i_size]='\0'; /* Just to make sure */ + p_announce->p_sap = NULL; + vlc_object_attach( p_announce, p_this->p_libvlc); - return psz_service; - + return p_announce; } -/***************************************************************************** - * sout_SLPReport: Reporting function. Unused at the moment but needed - *****************************************************************************/ -#ifdef HAVE_SLP_H -void sout_SLPReport(SLPHandle slp_handle,SLPError slp_error,void* cookie) -{ -} -#endif - -/***************************************************************************** - * sout_SLPReg: Registers the program with SLP - *****************************************************************************/ -int sout_SLPReg( sout_instance_t *p_sout, char * psz_url, - char * psz_name) +/** + * Destroy a announce handler object + * + * \param p_announce the announce handler to destroy + * \return VLC_SUCCESS or an error + */ +int announce_HandlerDestroy( announce_handler_t *p_announce ) { -#ifdef HAVE_SLP_H - SLPHandle slp_handle; - SLPError slp_res; - char *psz_service= sout_SLPBuildName(psz_url,psz_name); - - if( SLPOpen( NULL, SLP_FALSE, &slp_handle ) != SLP_OK) - { - msg_Warn(p_sout,"Unable to initialize SLP"); - return -1; - } - - msg_Info(p_sout , "Registering %s (name: %s) in SLP", - psz_service , psz_name); - - slp_res = SLPReg ( slp_handle, - psz_service, - SLP_LIFETIME_MAXIMUM, - NULL, - psz_name, - SLP_TRUE, - sout_SLPReport, - NULL ); - - if( slp_res != SLP_OK ) + if( p_announce->p_sap ) { - msg_Warn(p_sout,"Error while registering service: %i", slp_res ); - return -1; + /* Exit the SAP */ + vlc_object_release( p_announce->p_sap ); } - return 0; + /* Free the structure */ + vlc_object_release( p_announce ); -#else /* This function should never be called if this is false */ - return -1 -#endif + return VLC_SUCCESS; } - -/***************************************************************************** - * sout_SLDePReg: Unregisters the program from SLP - *****************************************************************************/ -int sout_SLPDereg( sout_instance_t *p_sout, char * psz_url, - char * psz_name) +/* Register an announce */ +static int announce_Register( announce_handler_t *p_announce, + session_descriptor_t *p_session, + announce_method_t *p_method ) { -#ifdef HAVE_SLP_H + if (p_method == NULL) + return VLC_EGENERIC; - SLPHandle slp_handle; - SLPError slp_res; - char *psz_service= sout_SLPBuildName(psz_url,psz_name); - - if( SLPOpen( NULL, SLP_FALSE, &slp_handle ) != SLP_OK) + msg_Dbg( p_announce, "registering announce"); + if( p_method == &sap_method ) { - msg_Warn(p_sout,"Unable to initialize SLP"); - return -1; - } - - msg_Info(p_sout , "Unregistering %s from SLP", - psz_service); - - slp_res = SLPDereg ( slp_handle, - psz_service, - sout_SLPReport, - NULL ); - - if( slp_res != SLP_OK ) + /* Do we already have a SAP announce handler ? */ + if( !p_announce->p_sap ) + { + sap_handler_t *p_sap = announce_SAPHandlerCreate( p_announce ); + msg_Dbg( p_announce, "creating SAP announce handler"); + if( !p_sap ) + { + msg_Err( p_announce, "SAP handler creation failed" ); + return VLC_ENOOBJ; + } + p_announce->p_sap = p_sap; + } + /* this will set p_session->p_sap for later deletion */ + msg_Dbg( p_announce, "adding SAP session"); + p_announce->p_sap->pf_add( p_announce->p_sap, p_session ); + } + else { - msg_Warn(p_sout,"Error while registering service: %i", slp_res ); - return -1; + msg_Err( p_announce, "announce type unsupported" ); + return VLC_EGENERIC; } + return VLC_SUCCESS; +} - return 0; -#else /* This function should never be called if this is false */ - return -1 -#endif +/* Unregister an announce */ +static int announce_UnRegister( announce_handler_t *p_announce, + session_descriptor_t *p_session ) +{ + msg_Dbg( p_announce, "unregistering announce" ); + if( p_announce->p_sap ) + p_announce->p_sap->pf_del( p_announce->p_sap, p_session ); + return VLC_SUCCESS; }