X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fstream_output%2Fannounce.c;h=e7950f94d3b0303962e1f0ed3fa3d44683e8d024;hb=7681a9b5d1df0cbcaa5914cc33bb40fee8276a36;hp=14fb630f4d7090e145b9b7377a3a126ebce5dd96;hpb=7c0d8351cb4be37592af1d659de6e9c6beae7a6c;p=vlc diff --git a/src/stream_output/announce.c b/src/stream_output/announce.c index 14fb630f4d..e7950f94d3 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,7 +18,7 @@ * * 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. *****************************************************************************/ /***************************************************************************** @@ -27,249 +27,331 @@ #include /* free() */ #include /* sprintf() */ #include /* strerror() */ - -#ifdef HAVE_UNISTD_H -# include -#endif +#include #include +#include +#include /* FIXME: fix RegisterSDP() and remove this */ +#include "stream_output.h" -#include -#undef DEBUG_BUFFER - -#include -#include - -#define SAP_IPV4_ADDR "224.2.127.254" /* Standard port and address for SAP */ -#define SAP_PORT 9875 +struct announce_method_t +{ +} sap_method; -#define SAP_IPV6_ADDR_1 "FF0" -#define SAP_IPV6_ADDR_2 "::2:7FFE" +/**************************************************************************** + * Sout-side functions + ****************************************************************************/ -/***************************************************************************** - * sout_SAPNew: Creates a SAP Session - *****************************************************************************/ -sap_session_t * sout_SAPNew ( sout_instance_t *p_sout , - char * psz_url_arg , char *psz_port_arg , - char * psz_name_arg, int ip_version, - char * psz_v6_scope ) +/** + * Register a new session with the announce handler + * + * \param p_sout a sout instance structure + * \param p_session a session descriptor + * \param p_method an announce method descriptor + * \return VLC_SUCCESS or an error + */ +int sout_AnnounceRegister( sout_instance_t *p_sout, + session_descriptor_t *p_session, + announce_method_t *p_method ) { - sap_session_t *p_new; /* The SAP structure */ - module_t *p_network; /* Network module */ - network_socket_t socket_desc; /* Socket descriptor */ - char psz_network[6]; /* IPv4 or IPv6 */ - char *sap_ipv6_addr=NULL; /* IPv6 built address */ - - /* Allocate the SAP structure */ - p_new = (sap_session_t *)malloc( sizeof ( sap_session_t ) ) ; - if ( !p_new ) + int 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, "No memory left" ); - return NULL; + msg_Dbg( p_sout, "No announce handler found, creating one" ); + p_announce = announce_HandlerCreate( p_sout ); + if( !p_announce ) + { + msg_Err( p_sout, "Creation failed" ); + return VLC_ENOMEM; + } + vlc_object_yield( p_announce ); + msg_Dbg( p_sout, "creation done" ); } - /* Fill the information in the structure */ - sprintf ( p_new->psz_url , "%s" , psz_url_arg ); - sprintf ( p_new->psz_name , "%s" , psz_name_arg ); - /* Port is not implemented in sout */ - sprintf ( p_new->psz_port, "%s" , psz_port_arg ); + i_ret = announce_Register( p_announce, p_session, p_method ); + vlc_object_release( p_announce ); - p_new->i_ip_version = ip_version; + return i_ret; +} - /* Only "6" triggers IPv6. IPv4 is default */ - if( ip_version != 6 ) +/** + * 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_uri 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 *cfgpref, + const char *psz_sdp, const char *psz_uri, + announce_method_t *p_method ) +{ + 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 , "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; - - /* Call the network module */ - sprintf ( psz_network, "ipv4" ); - p_sout->p_private=(void*) &socket_desc; - if( !( p_network = module_Need( p_sout, "network", psz_network ) ) ) - { - msg_Warn( p_sout, "failed to open a connection (udp)" ); - return NULL; - } - module_Unneed( p_sout, p_network ); - - p_new->socket = socket_desc.i_handle; - if(p_new->socket <= 0 ) + msg_Dbg( p_sout, "no announce handler found, creating one" ); + p_announce = announce_HandlerCreate( p_sout ); + if( !p_announce ) { - msg_Warn( p_sout, "Unable to initialize SAP" ); + msg_Err( p_sout, "Creation failed" ); return NULL; } + vlc_object_yield( p_announce ); } - 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, "No memory left" ); - return NULL; - } - sprintf(sap_ipv6_addr,"%s%c%s", - SAP_IPV6_ADDR_1, - psz_v6_scope[0], - SAP_IPV6_ADDR_2); - - /* 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; - - sprintf ( psz_network, "ipv6" ); - - /* Call the network module */ - p_sout->p_private=(void*) &socket_desc; - if( !( p_network = module_Need( p_sout, "network", psz_network ) ) ) - { - msg_Warn( p_sout, "failed to open a connection (udp)" ); - return NULL; - } - module_Unneed( p_sout, p_network ); + p_session = sout_AnnounceSessionCreate(VLC_OBJECT (p_sout), cfgpref); + p_session->psz_sdp = strdup( psz_sdp ); - p_new->socket = socket_desc.i_handle; + /* GRUIK. We should not convert back-and-forth from string to numbers */ + struct addrinfo *res; + if (vlc_getaddrinfo (VLC_OBJECT (p_sout), psz_uri, 0, NULL, &res) == 0) + { + if (res->ai_addrlen <= sizeof (p_session->addr)) + memcpy (&p_session->addr, res->ai_addr, + p_session->addrlen = res->ai_addrlen); + vlc_freeaddrinfo (res); + } - if(p_new->socket <= 0 ) - { - msg_Warn( p_sout, "Unable to initialize SAP" ); - return NULL; - } + announce_Register( p_announce, p_session, p_method ); - /* Free what we allocated */ - if( sap_ipv6_addr ) free(sap_ipv6_addr); + vlc_object_release( p_announce ); + return p_session; +} + +/** + * 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; + 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, "unable to remove announce: no announce handler" ); + return VLC_ENOOBJ; } + i_ret = announce_UnRegister( p_announce, p_session ); - msg_Dbg (p_sout,"SAP initialization complete"); + vlc_object_release( p_announce ); - return(p_new); + return i_ret; } -/***************************************************************************** - * sout_SAPDelete: Deletes a SAP Session - *****************************************************************************/ -void sout_SAPDelete( sout_instance_t *p_sout , sap_session_t * p_this ) +/** + * Create and initialize a session descriptor + * + * \return a new session descriptor + */ +session_descriptor_t * sout_AnnounceSessionCreate (vlc_object_t *obj, + const char *cfgpref) { - if( close(p_this->socket) ) + size_t cfglen = strlen (cfgpref); + if (cfglen > 100) + return NULL; + + char varname[cfglen + sizeof ("description")], *subvar = varname + cfglen; + strcpy (varname, cfgpref); + + session_descriptor_t *p_session = calloc (1, sizeof (*p_session)); + if (p_session == NULL) + return NULL; + + strcpy (subvar, "name"); + p_session->psz_name = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "group"); + p_session->psz_group = var_GetNonEmptyString (obj, varname); + + strcpy (subvar, "description"); + p_session->description = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "url"); + p_session->url = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "email"); + p_session->email = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "phone"); + p_session->phone = var_GetNonEmptyString (obj, varname); + + return p_session; +} + +int sout_SessionSetMedia (vlc_object_t *obj, session_descriptor_t *p_session, + const char *fmt, const char *src, int sport, + const char *dst, int dport) +{ + if ((p_session->sdpformat = strdup (fmt)) == NULL) + return VLC_ENOMEM; + + /* GRUIK. We should not convert back-and-forth from string to numbers */ + struct addrinfo *res; + if (vlc_getaddrinfo (obj, dst, dport, NULL, &res) == 0) + { + if (res->ai_addrlen > sizeof (p_session->addr)) + goto oflow; + + memcpy (&p_session->addr, res->ai_addr, + p_session->addrlen = res->ai_addrlen); + vlc_freeaddrinfo (res); + } + if (vlc_getaddrinfo (obj, src, sport, NULL, &res) == 0) { - msg_Err ( p_sout, "Unable to close SAP socket"); + if (res->ai_addrlen > sizeof (p_session->orig)) + goto oflow; + memcpy (&p_session->orig, res->ai_addr, + p_session->origlen = res->ai_addrlen); + vlc_freeaddrinfo (res); } + return 0; - if( p_this ) free( p_this ); +oflow: + vlc_freeaddrinfo (res); + return VLC_ENOMEM; } -/***************************************************************************** - * sout_SAPSend: Sends a SAP packet - *****************************************************************************/ -void sout_SAPSend( sout_instance_t *p_sout, sap_session_t * p_this) +/** + * Destroy a session descriptor and free all + * + * \param p_session the session to destroy + * \return Nothing + */ +void sout_AnnounceSessionDestroy( session_descriptor_t *p_session ) { - char *sap_head; /* SAP header */ - char sap_msg[1000]; /* SDP content */ - char *sap_send; /* What we send */ - char *payload_type="application/sdp"; - int i_send_result=0; /* Result of send */ - int i; - int i_header_size; /* SAP header size */ - int i_msg_size; /* SDP content size */ - int i_size; /* Total size */ - - /* We send a packet every 24 calls to the function */ - if( p_this->sendnow == 24 ) + if( p_session ) { - i_header_size = 9 + strlen( payload_type ); - sap_head = ( char * )malloc( i_header_size * sizeof( char ) ); + free (p_session->psz_name); + free (p_session->psz_group); + free (p_session->psz_sdp); + free (p_session->description); + free (p_session->sdpformat); + free (p_session->url); + free (p_session->email); + free (p_session->phone); + free( p_session ); + } +} - if( ! sap_head ) - { - msg_Warn( p_sout , "No memory left"); - return; - } +/** + * \return the SAP announce method + */ +announce_method_t * sout_SAPMethod (void) +{ + return &sap_method; +} - /* Create the SAP headers */ - sap_head[0]=0x20; /* Means IPv4, not encrypted, not compressed */ - sap_head[1]=0x00; /* No authentification */ - sap_head[2]=0x42; /* Version */ - sap_head[3]=0x12; /* Version */ - - sap_head[4]=0x01; /* Source IP FIXME: we should get the real address */ - sap_head[5]=0x02; /* idem */ - sap_head[6]=0x03; /* idem */ - sap_head[7]=0x04; /* idem */ - - strncpy( sap_head+8 , payload_type , 15 ); - sap_head[ i_header_size-1 ] = '\0'; - - /* Create the SDP content */ - /* Do not add spaces at beginning of the lines ! */ - sprintf( sap_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_this->psz_name , p_this->psz_port , p_this->psz_url ); - - i_msg_size = strlen( sap_msg ); - i_size = i_msg_size + i_header_size; - - /* Create the message */ - sap_send = ( char* )malloc( i_size*sizeof(char) ); - if( !sap_send ) - { - msg_Err( p_sout , "No memory left") ; - return; - } +void sout_MethodRelease (announce_method_t *m) +{ + assert (m == &sap_method); +} - for( i = 0 ; i < i_header_size ; i++ ) - { - sap_send[i] = sap_head[i]; - } +/************************************************************************ + * Announce handler functions (private) + ************************************************************************/ - for( ; i < i_size ; i++ ) - { - sap_send[i] = sap_msg[i-i_header_size]; - } +/** + * Create the announce handler object + * + * \param p_this a vlc_object structure + * \return the new announce handler or NULL on error + */ +announce_handler_t *__announce_HandlerCreate( vlc_object_t *p_this ) +{ + announce_handler_t *p_announce; + + p_announce = vlc_object_create( p_this, VLC_OBJECT_ANNOUNCE ); + + if( !p_announce ) + { + msg_Err( p_this, "out of memory" ); + return NULL; + } + + p_announce->p_sap = NULL; + vlc_object_attach( p_announce, p_this->p_libvlc); + + return p_announce; +} + +/** + * 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 ) +{ + if( p_announce->p_sap ) + { + ((vlc_object_t *)p_announce->p_sap)->b_die = VLC_TRUE; + /* Wait for the SAP thread to exit */ + vlc_thread_join( (vlc_object_t *)p_announce->p_sap ); + announce_SAPHandlerDestroy( p_announce->p_sap ); + } + + /* Free the structure */ + vlc_object_destroy( p_announce ); - if( i_size < 1024 ) /* We mustn't send packets larger than 1024B */ + return VLC_SUCCESS; +} + +/* Register an announce */ +int announce_Register( announce_handler_t *p_announce, + session_descriptor_t *p_session, + announce_method_t *p_method ) +{ + if (p_method == NULL) + return VLC_EGENERIC; + + msg_Dbg( p_announce, "registering announce"); + if( p_method == &sap_method ) + { + /* Do we already have a SAP announce handler ? */ + if( !p_announce->p_sap ) { - if( p_this->i_ip_version == 6) + sap_handler_t *p_sap = announce_SAPHandlerCreate( p_announce ); + msg_Dbg( p_announce, "creating SAP announce handler"); + if( !p_sap ) { - i_send_result = send( p_this->socket, sap_send, i_size, 0 ); + msg_Err( p_announce, "SAP handler creation failed" ); + return VLC_ENOOBJ; } - else - { - i_send_result = send( p_this->socket, sap_send, i_size, 0 ); - } - } - - if( i_send_result == -1 ) - { - msg_Warn(p_sout, "SAP send failed on socket %i", p_this->socket ); - perror("sendto"); + p_announce->p_sap = p_sap; } - - p_this->sendnow = 0; - - /* Free what we allocated */ - if(sap_send) free(sap_send); - if(sap_head) free(sap_head); + /* 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_Err( p_announce, "announce type unsupported" ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + - p_this->sendnow++; +/* Unregister an announce */ +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; }