+/****************************************************************************
+ * SAP:
+ ****************************************************************************/
+static int SapSetup( sout_stream_t *p_stream )
+{
+ sout_stream_sys_t *p_sys = p_stream->p_sys;
+ sout_instance_t *p_sout = p_stream->p_sout;
+ announce_method_t *p_method = sout_AnnounceMethodCreate( METHOD_TYPE_SAP );
+
+ /* Remove the previous session */
+ if( p_sys->p_session != NULL)
+ {
+ sout_AnnounceUnRegister( p_sout, p_sys->p_session);
+ sout_AnnounceSessionDestroy( p_sys->p_session );
+ p_sys->p_session = NULL;
+ }
+
+ if( ( p_sys->i_es > 0 || p_sys->p_mux ) && p_sys->psz_sdp && *p_sys->psz_sdp )
+ {
+ p_sys->p_session = sout_AnnounceRegisterSDP( p_sout, p_sys->psz_sdp,
+ p_sys->psz_destination,
+ p_method );
+ }
+
+ free( p_method );
+ return VLC_SUCCESS;
+}
+
+/****************************************************************************
+* File:
+****************************************************************************/
+static int FileSetup( sout_stream_t *p_stream )
+{
+ sout_stream_sys_t *p_sys = p_stream->p_sys;
+ FILE *f;
+
+ if( ( f = utf8_fopen( p_sys->psz_sdp_file, "wt" ) ) == NULL )
+ {
+ msg_Err( p_stream, "cannot open file '%s' (%s)",
+ p_sys->psz_sdp_file, strerror(errno) );
+ return VLC_EGENERIC;
+ }
+
+ fprintf( f, "%s", p_sys->psz_sdp );
+ fclose( f );
+
+ return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * HTTP:
+ ****************************************************************************/
+static int HttpCallback( httpd_file_sys_t *p_args,
+ httpd_file_t *, uint8_t *p_request,
+ uint8_t **pp_data, int *pi_data );
+
+static int HttpSetup( sout_stream_t *p_stream, vlc_url_t *url)
+{
+ sout_stream_sys_t *p_sys = p_stream->p_sys;
+
+ p_sys->p_httpd_host = httpd_HostNew( VLC_OBJECT(p_stream), url->psz_host, url->i_port > 0 ? url->i_port : 80 );
+ if( p_sys->p_httpd_host )
+ {
+ p_sys->p_httpd_file = httpd_FileNew( p_sys->p_httpd_host,
+ url->psz_path ? url->psz_path : "/",
+ "application/sdp",
+ NULL, NULL, NULL,
+ HttpCallback, (void*)p_sys );
+ }
+ if( p_sys->p_httpd_file == NULL )
+ {
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+static int HttpCallback( httpd_file_sys_t *p_args,
+ httpd_file_t *f, uint8_t *p_request,
+ uint8_t **pp_data, int *pi_data )
+{
+ sout_stream_sys_t *p_sys = (sout_stream_sys_t*)p_args;
+
+ vlc_mutex_lock( &p_sys->lock_sdp );
+ if( p_sys->psz_sdp && *p_sys->psz_sdp )
+ {
+ *pi_data = strlen( p_sys->psz_sdp );
+ *pp_data = malloc( *pi_data );
+ memcpy( *pp_data, p_sys->psz_sdp, *pi_data );
+ }
+ else
+ {
+ *pp_data = NULL;
+ *pi_data = 0;
+ }
+ vlc_mutex_unlock( &p_sys->lock_sdp );
+
+ return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * RTSP:
+ ****************************************************************************/
+static rtsp_client_t *RtspClientNew( sout_stream_t *p_stream, char *psz_session )
+{
+ rtsp_client_t *rtsp = malloc( sizeof( rtsp_client_t ));
+
+ rtsp->psz_session = psz_session;
+ rtsp->i_last = 0;
+ rtsp->b_playing = VLC_FALSE;
+ rtsp->i_id = 0;
+ rtsp->id = NULL;
+ rtsp->i_access = 0;
+ rtsp->access = NULL;
+
+ TAB_APPEND( p_stream->p_sys->i_rtsp, p_stream->p_sys->rtsp, rtsp );
+
+ return rtsp;
+}
+static rtsp_client_t *RtspClientGet( sout_stream_t *p_stream, char *psz_session )
+{
+ int i;
+
+ if( psz_session ) return NULL;
+
+ for( i = 0; i < p_stream->p_sys->i_rtsp; i++ )
+ {
+ if( !strcmp( p_stream->p_sys->rtsp[i]->psz_session, psz_session ) )
+ {
+ return p_stream->p_sys->rtsp[i];
+ }
+ }
+ return NULL;
+}
+
+static void RtspClientDel( sout_stream_t *p_stream, rtsp_client_t *rtsp )
+{
+ int i;
+ TAB_REMOVE( p_stream->p_sys->i_rtsp, p_stream->p_sys->rtsp, rtsp );
+
+ for( i = 0; i < rtsp->i_access; i++ )
+ {
+ sout_AccessOutDelete( rtsp->access[i] );
+ }
+ if( rtsp->id ) free( rtsp->id );
+ if( rtsp->access ) free( rtsp->access );
+
+ free( rtsp->psz_session );
+ free( rtsp );
+}
+
+static int RtspSetup( sout_stream_t *p_stream, vlc_url_t *url )
+{
+ sout_stream_sys_t *p_sys = p_stream->p_sys;
+
+ msg_Dbg( p_stream, "rtsp setup: %s : %d / %s\n", url->psz_host, url->i_port, url->psz_path );
+
+ p_sys->p_rtsp_host = httpd_HostNew( VLC_OBJECT(p_stream), url->psz_host, url->i_port > 0 ? url->i_port : 554 );
+ if( p_sys->p_rtsp_host == NULL )
+ {
+ return VLC_EGENERIC;
+ }
+
+ p_sys->psz_rtsp_path = strdup( url->psz_path ? url->psz_path : "/" );
+ p_sys->psz_rtsp_control = malloc (strlen( url->psz_host ) + 20 + strlen( p_sys->psz_rtsp_path ) + 1 );
+ sprintf( p_sys->psz_rtsp_control, "rtsp://%s:%d%s",
+ url->psz_host, url->i_port > 0 ? url->i_port : 554, p_sys->psz_rtsp_path );
+
+ p_sys->p_rtsp_url = httpd_UrlNewUnique( p_sys->p_rtsp_host, p_sys->psz_rtsp_path, NULL, NULL, NULL );
+ if( p_sys->p_rtsp_url == 0 )
+ {
+ return VLC_EGENERIC;
+ }
+ httpd_UrlCatch( p_sys->p_rtsp_url, HTTPD_MSG_DESCRIBE, RtspCallback, (void*)p_stream );
+ httpd_UrlCatch( p_sys->p_rtsp_url, HTTPD_MSG_PLAY, RtspCallback, (void*)p_stream );
+ httpd_UrlCatch( p_sys->p_rtsp_url, HTTPD_MSG_PAUSE, RtspCallback, (void*)p_stream );
+ httpd_UrlCatch( p_sys->p_rtsp_url, HTTPD_MSG_TEARDOWN, RtspCallback, (void*)p_stream );
+
+ return VLC_SUCCESS;
+}
+
+static int RtspCallback( httpd_callback_sys_t *p_args,
+ httpd_client_t *cl,
+ httpd_message_t *answer, httpd_message_t *query )
+{
+ sout_stream_t *p_stream = (sout_stream_t*)p_args;
+ sout_stream_sys_t *p_sys = p_stream->p_sys;
+ char *psz_destination = p_sys->psz_destination;
+ char *psz_session = NULL;
+ char *psz_cseq = NULL;
+ int i_cseq = 0;
+
+ if( answer == NULL || query == NULL )
+ {
+ return VLC_SUCCESS;
+ }
+ //fprintf( stderr, "RtspCallback query: type=%d\n", query->i_type );
+
+ answer->i_proto = HTTPD_PROTO_RTSP;
+ answer->i_version= query->i_version;
+ answer->i_type = HTTPD_MSG_ANSWER;
+
+ switch( query->i_type )
+ {
+ case HTTPD_MSG_DESCRIBE:
+ {
+ char *psz_sdp = SDPGenerate( p_stream, psz_destination ? psz_destination : "0.0.0.0", VLC_TRUE );
+
+ answer->i_status = 200;
+ answer->psz_status = strdup( "OK" );
+ httpd_MsgAdd( answer, "Content-type", "%s", "application/sdp" );
+ httpd_MsgAdd( answer, "Content-Base", "%s/", p_sys->psz_rtsp_control );
+ answer->p_body = (uint8_t *)psz_sdp;
+ answer->i_body = strlen( psz_sdp );
+ break;
+ }
+
+ case HTTPD_MSG_PLAY:
+ {
+ rtsp_client_t *rtsp;
+ /* for now only multicast so easy */
+ answer->i_status = 200;
+ answer->psz_status = strdup( "OK" );
+ answer->i_body = 0;
+ answer->p_body = NULL;
+
+ psz_session = httpd_MsgGet( query, "Session" );
+ rtsp = RtspClientGet( p_stream, psz_session );
+ if( rtsp && !rtsp->b_playing )
+ {
+ int i_id;
+ /* FIXME */
+ rtsp->b_playing = VLC_TRUE;
+
+ vlc_mutex_lock( &p_sys->lock_es );
+ for( i_id = 0; i_id < rtsp->i_id; i_id++ )
+ {
+ sout_stream_id_t *id = rtsp->id[i_id];
+ int i;
+
+ for( i = 0; i < p_sys->i_es; i++ )
+ {
+ if( id == p_sys->es[i] )
+ break;
+ }
+ if( i >= p_sys->i_es ) continue;
+
+ vlc_mutex_lock( &id->lock_rtsp );
+ TAB_APPEND( id->i_rtsp_access, id->rtsp_access, rtsp->access[i_id] );
+ vlc_mutex_unlock( &id->lock_rtsp );
+ }
+ vlc_mutex_unlock( &p_sys->lock_es );
+ }
+ break;
+ }
+ case HTTPD_MSG_PAUSE:
+ /* FIXME */
+ return VLC_EGENERIC;
+ case HTTPD_MSG_TEARDOWN:
+ {
+ rtsp_client_t *rtsp;
+
+ /* for now only multicast so easy again */
+ answer->i_status = 200;
+ answer->psz_status = strdup( "OK" );
+ answer->i_body = 0;
+ answer->p_body = NULL;
+
+ psz_session = httpd_MsgGet( query, "Session" );
+ rtsp = RtspClientGet( p_stream, psz_session );
+ if( rtsp )
+ {
+ int i_id;
+
+ vlc_mutex_lock( &p_sys->lock_es );
+ for( i_id = 0; i_id < rtsp->i_id; i_id++ )
+ {
+ sout_stream_id_t *id = rtsp->id[i_id];
+ int i;
+
+ for( i = 0; i < p_sys->i_es; i++ )
+ {
+ if( id == p_sys->es[i] )
+ break;
+ }
+ if( i >= p_sys->i_es ) continue;
+
+ vlc_mutex_lock( &id->lock_rtsp );
+ TAB_REMOVE( id->i_rtsp_access, id->rtsp_access, rtsp->access[i_id] );
+ vlc_mutex_unlock( &id->lock_rtsp );
+ }
+ vlc_mutex_unlock( &p_sys->lock_es );
+
+ RtspClientDel( p_stream, rtsp );
+ }
+ break;
+ }
+
+ default:
+ return VLC_EGENERIC;
+ }
+ httpd_MsgAdd( answer, "Server", PACKAGE_STRING );
+ httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
+ psz_cseq = httpd_MsgGet( query, "Cseq" );
+ if( psz_cseq )
+ i_cseq = atoi( psz_cseq );
+ else
+ i_cseq = 0;
+ httpd_MsgAdd( answer, "Cseq", "%d", i_cseq );
+ httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
+
+ if( psz_session )
+ {
+ httpd_MsgAdd( answer, "Session", "%s;timeout=5", psz_session );
+ }
+ return VLC_SUCCESS;
+}
+
+static int RtspCallbackId( httpd_callback_sys_t *p_args,
+ httpd_client_t *cl,
+ httpd_message_t *answer, httpd_message_t *query )
+{
+ sout_stream_id_t *id = (sout_stream_id_t*)p_args;
+ sout_stream_t *p_stream = id->p_stream;
+ sout_stream_sys_t *p_sys = p_stream->p_sys;
+ char *psz_session = NULL;
+ char *psz_cseq = NULL;
+ int i_cseq = 0;
+
+
+ if( answer == NULL || query == NULL )
+ {
+ return VLC_SUCCESS;
+ }
+ //fprintf( stderr, "RtspCallback query: type=%d\n", query->i_type );
+
+ answer->i_proto = HTTPD_PROTO_RTSP;
+ answer->i_version= query->i_version;
+ answer->i_type = HTTPD_MSG_ANSWER;
+
+ switch( query->i_type )
+ {
+ case HTTPD_MSG_SETUP:
+ {
+ char *psz_transport = httpd_MsgGet( query, "Transport" );
+
+ //fprintf( stderr, "HTTPD_MSG_SETUP: transport=%s\n", psz_transport );
+
+ if( strstr( psz_transport, "multicast" ) && id->psz_destination )
+ {
+ //fprintf( stderr, "HTTPD_MSG_SETUP: multicast\n" );
+ answer->i_status = 200;
+ answer->psz_status = strdup( "OK" );
+ answer->i_body = 0;
+ answer->p_body = NULL;
+ psz_session = httpd_MsgGet( query, "Session" );
+ if( !psz_session )
+ {
+ psz_session = malloc( 100 );
+ sprintf( psz_session, "%d", rand() );
+ }
+ httpd_MsgAdd( answer, "Transport",
+ "RTP/AVP/UDP;destination=%s;port=%d-%d;ttl=%d",
+ id->psz_destination, id->i_port,id->i_port+1,
+ p_sys->i_ttl );
+ }
+ else if( strstr( psz_transport, "unicast" ) && strstr( psz_transport, "client_port=" ) )
+ {
+ int i_port = atoi( strstr( psz_transport, "client_port=" ) + strlen("client_port=") );
+ char ip[NI_MAXNUMERICHOST], psz_access[17], psz_url[NI_MAXNUMERICHOST + 8];
+
+ sout_access_out_t *p_access;
+
+ rtsp_client_t *rtsp = NULL;
+
+ if( httpd_ClientIP( cl, ip ) == NULL )
+ {
+ answer->i_status = 500;
+ answer->psz_status = strdup( "Internal server error" );
+ answer->i_body = 0;
+ answer->p_body = NULL;
+ break;
+ }
+
+ //fprintf( stderr, "HTTPD_MSG_SETUP: unicast ip=%s port=%d\n", ip, i_port );
+
+ psz_session = httpd_MsgGet( query, "Session" );
+ if( !psz_session )
+ {
+ psz_session = malloc( 100 );
+ sprintf( psz_session, "%d", rand() );
+
+ rtsp = RtspClientNew( p_stream, psz_session );
+ }
+ else
+ {
+ rtsp = RtspClientGet( p_stream, psz_session );
+ if( rtsp == NULL )
+ {
+ answer->i_status = 454;
+ answer->psz_status = strdup( "Unknown session id" );
+ answer->i_body = 0;
+ answer->p_body = NULL;
+ break;
+ }
+ }
+
+ /* first try to create the access out */
+ if( p_sys->i_ttl )
+ snprintf( psz_access, sizeof( psz_access ),
+ "udp{raw,ttl=%d}", p_sys->i_ttl );
+ else
+ strlcpy( psz_access, "udp{raw}", sizeof( psz_access ) );
+
+ snprintf( psz_url, sizeof( psz_url ),
+ ( strchr( ip, ':' ) != NULL ) ? "[%s]:%d" : "%s:%d",
+ ip, i_port );
+
+ if( ( p_access = sout_AccessOutNew( p_stream->p_sout, psz_access, psz_url ) ) == NULL )
+ {
+ msg_Err( p_stream, "cannot create the access out for %s://%s",
+ psz_access, psz_url );
+ answer->i_status = 500;
+ answer->psz_status = strdup( "Internal server error" );
+ answer->i_body = 0;
+ answer->p_body = NULL;
+ break;
+ }