From 21a5355e90f0c957c727201e5686c7892754c5c5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9mi=20Denis-Courmont?= Date: Thu, 23 Aug 2007 16:40:00 +0000 Subject: [PATCH] Split RTP sout into multiple pieces. Even then rtp.c is quite big... --- modules/stream_out/Modules.am | 2 +- modules/stream_out/rtp.c | 550 +------------------------------ modules/stream_out/rtp.h | 74 +++++ modules/stream_out/rtsp.c | 603 ++++++++++++++++++++++++++++++++++ 4 files changed, 681 insertions(+), 548 deletions(-) create mode 100644 modules/stream_out/rtp.h create mode 100644 modules/stream_out/rtsp.c diff --git a/modules/stream_out/Modules.am b/modules/stream_out/Modules.am index 74e9aef0d9..7b85fc773b 100644 --- a/modules/stream_out/Modules.am +++ b/modules/stream_out/Modules.am @@ -6,7 +6,7 @@ SOURCES_stream_out_duplicate = duplicate.c SOURCES_stream_out_es = es.c SOURCES_stream_out_display = display.c SOURCES_stream_out_gather = gather.c -SOURCES_stream_out_rtp = rtp.c +SOURCES_stream_out_rtp = rtp.c rtp.h rtsp.c SOURCES_stream_out_switcher = switcher.c SOURCES_stream_out_bridge = bridge.c SOURCES_stream_out_mosaic_bridge = mosaic_bridge.c diff --git a/modules/stream_out/rtp.c b/modules/stream_out/rtp.c index 92e34e7bd2..b5e75b8059 100644 --- a/modules/stream_out/rtp.c +++ b/modules/stream_out/rtp.c @@ -41,6 +41,8 @@ #include #include +#include "rtp.h" + /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -156,22 +158,6 @@ static int Del ( sout_stream_t *, sout_stream_id_t * ); static int Send( sout_stream_t *, sout_stream_id_t *, block_t* ); -/* For unicast/interleaved streaming */ -typedef struct -{ - char *psz_session; - int64_t i_last; /* for timeout */ - - /* is it in "play" state */ - vlc_bool_t b_playing; - - /* output (id-access) */ - int i_id; - sout_stream_id_t **id; - int i_access; - sout_access_out_t **access; -} rtsp_client_t; - struct sout_stream_sys_t { /* sdp */ @@ -231,45 +217,6 @@ struct sout_stream_sys_t rtsp_client_t **rtsp; }; -typedef int (*pf_rtp_packetizer_t)( sout_stream_t *, sout_stream_id_t *, - block_t * ); - -struct sout_stream_id_t -{ - sout_stream_t *p_stream; - /* rtp field */ - uint8_t i_payload_type; - uint16_t i_sequence; - uint32_t i_timestamp_start; - uint8_t ssrc[4]; - - /* for sdp */ - int i_clock_rate; - char *psz_rtpmap; - char *psz_fmtp; - char *psz_destination; - int i_port; - int i_cat; - int i_bitrate; - - /* Packetizer specific fields */ - pf_rtp_packetizer_t pf_packetize; - int i_mtu; - - /* for sending the packets */ - sout_access_out_t *p_access; - - vlc_mutex_t lock_rtsp; - int i_rtsp_access; - sout_access_out_t **rtsp_access; - - /* */ - sout_input_t *p_input; - - /* RTSP url control */ - httpd_url_t *p_rtsp_url; -}; - static int AccessOutGrabberWrite( sout_access_out_t *, block_t * ); static void SDPHandleUrl( sout_stream_t *, char * ); @@ -277,17 +224,6 @@ static void SDPHandleUrl( sout_stream_t *, char * ); static int SapSetup( sout_stream_t *p_stream ); static int FileSetup( sout_stream_t *p_stream ); static int HttpSetup( sout_stream_t *p_stream, vlc_url_t * ); -static int RtspSetup( sout_stream_t *p_stream, vlc_url_t * ); - -static int RtspCallback( httpd_callback_sys_t *, httpd_client_t *, - httpd_message_t *, httpd_message_t * ); -static int RtspCallbackId( httpd_callback_sys_t *, httpd_client_t *, - httpd_message_t *, httpd_message_t * ); - - -static rtsp_client_t *RtspClientNew( sout_stream_t *, const char *psz_session ); -static rtsp_client_t *RtspClientGet( sout_stream_t *, const char *psz_session ); -static void RtspClientDel( sout_stream_t *, rtsp_client_t * ); /***************************************************************************** * Open: @@ -760,7 +696,7 @@ static void SDPHandleUrl( sout_stream_t *p_stream, char *psz_url ) a= charset: (normally charset should be UTF-8, this can be used to override s= and i=) a= x-plgroup: (missing) RTP packets need to get the correct src IP address */ -static char *SDPGenerate( const sout_stream_t *p_stream, +/*static*/ char *SDPGenerate( const sout_stream_t *p_stream, const char *psz_destination, vlc_bool_t b_rtsp ) { sout_stream_sys_t *p_sys = p_stream->p_sys; @@ -1538,486 +1474,6 @@ static int HttpCallback( httpd_file_sys_t *p_args, return VLC_SUCCESS; } -/**************************************************************************** - * RTSP: - ****************************************************************************/ -static rtsp_client_t *RtspClientNew( sout_stream_t *p_stream, const char *psz_session ) -{ - rtsp_client_t *rtsp = malloc( sizeof( rtsp_client_t )); - - rtsp->psz_session = strdup( 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, const 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_SETUP, 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; - const char *psz_session = NULL; - const char *psz_cseq = NULL; - - 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; - answer->i_body = 0; - answer->p_body = NULL; - - if( httpd_MsgGet( query, "Require" ) != NULL ) - { - answer->i_status = 551; - httpd_MsgAdd( query, "Unsupported", "%s", - httpd_MsgGet( query, "Require" ) ); - } - else - switch( query->i_type ) - { - case HTTPD_MSG_DESCRIBE: - { - char *psz_sdp = SDPGenerate( p_stream, psz_destination, VLC_TRUE ); - - answer->i_status = 200; - 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_SETUP: - answer->i_status = 459; - break; - - case HTTPD_MSG_PLAY: - { - rtsp_client_t *rtsp; - /* for now only multicast so easy */ - answer->i_status = 200; - - 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: - answer->i_status = 405; - httpd_MsgAdd( answer, "Allow", "DESCRIBE, PLAY, TEARDOWN" ); - break; - - case HTTPD_MSG_TEARDOWN: - { - rtsp_client_t *rtsp; - - /* for now only multicast so easy again */ - answer->i_status = 200; - - 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", "%s", PACKAGE_STRING ); - httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); - psz_cseq = httpd_MsgGet( query, "Cseq" ); - if( psz_cseq ) - httpd_MsgAdd( answer, "Cseq", "%s", psz_cseq ); - httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); - - if( psz_session ) - httpd_MsgAdd( answer, "Session", "%s;timeout=5", psz_session ); - return VLC_SUCCESS; -} - - -/** Finds the next transport choice */ -static inline const char *transport_next( const char *str ) -{ - /* Looks for comma */ - str = strchr( str, ',' ); - if( str == NULL ) - return NULL; /* No more transport options */ - - str++; /* skips comma */ - while( strchr( "\r\n\t ", *str ) ) - str++; - - return (*str) ? str : NULL; -} - - -/** Finds the next transport parameter */ -static inline const char *parameter_next( const char *str ) -{ - while( strchr( ",;", *str ) == NULL ) - str++; - - return (*str == ';') ? (str + 1) : NULL; -} - - -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_init[21]; - const char *psz_session; - const char *psz_cseq; - - 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; - answer->i_body = 0; - answer->p_body = NULL; - - /* Create new session ID if needed */ - psz_session = httpd_MsgGet( query, "Session" ); - if( psz_session == NULL ) - { - /* FIXME: should be somewhat secure randomness */ - snprintf( psz_session_init, sizeof(psz_session_init), I64Fd, - NTPtime64() + rand() ); - } - - if( httpd_MsgGet( query, "Require" ) != NULL ) - { - answer->i_status = 551; - httpd_MsgAdd( query, "Unsupported", "%s", - httpd_MsgGet( query, "Require" ) ); - } - else - switch( query->i_type ) - { - case HTTPD_MSG_SETUP: - { - answer->i_status = 461; - - for( const char *tpt = httpd_MsgGet( query, "Transport" ); - tpt != NULL; - tpt = transport_next( tpt ) ) - { - vlc_bool_t b_multicast = VLC_TRUE, b_unsupp = VLC_FALSE; - unsigned loport = 5004, hiport = 5005; /* from RFC3551 */ - - /* Check transport protocol. */ - /* Currently, we only support RTP/AVP over UDP */ - if( strncmp( tpt, "RTP/AVP", 7 ) ) - continue; - tpt += 7; - if( strncmp( tpt, "/UDP", 4 ) == 0 ) - tpt += 4; - if( strchr( ";,", *tpt ) == NULL ) - continue; - - /* Parse transport options */ - for( const char *opt = parameter_next( tpt ); - opt != NULL; - opt = parameter_next( opt ) ) - { - if( strncmp( opt, "multicast", 9 ) == 0) - b_multicast = VLC_TRUE; - else - if( strncmp( opt, "unicast", 7 ) == 0 ) - b_multicast = VLC_FALSE; - else - if( sscanf( opt, "client_port=%u-%u", &loport, &hiport ) == 2 ) - ; - else - if( strncmp( opt, "mode=", 5 ) == 0 ) - { - if( strncasecmp( opt + 5, "play", 4 ) - && strncasecmp( opt + 5, "\"PLAY\"", 6 ) ) - { - /* Not playing?! */ - b_unsupp = VLC_TRUE; - break; - } - } - else - { - /* - * Every other option is unsupported: - * - * "source" and "append" are invalid. - * - * For multicast, "port", "layers", "ttl" are set by the - * stream output configuration. - * - * For unicast, we do not allow "destination" as it - * carries a DoS risk, and we decide on "server_port". - * - * "interleaved" and "ssrc" are not implemented. - */ - b_unsupp = VLC_TRUE; - break; - } - } - - if( b_unsupp ) - continue; - - if( b_multicast ) - { - if( id->psz_destination == NULL ) - continue; - - answer->i_status = 200; - - httpd_MsgAdd( answer, "Transport", - "RTP/AVP/UDP;destination=%s;port=%d-%d;" - "ttl=%d;mode=play", - id->psz_destination, id->i_port, id->i_port+1, - ( p_sys->i_ttl > 0 ) ? p_sys->i_ttl : 1 ); - } - else - { - char ip[NI_MAXNUMERICHOST], psz_access[22], - url[NI_MAXNUMERICHOST + 8]; - sout_access_out_t *p_access; - rtsp_client_t *rtsp = NULL; - - if( ( hiport - loport ) > 1 ) - continue; - - if( psz_session == NULL ) - { - psz_session = psz_session_init; - rtsp = RtspClientNew( p_stream, psz_session ); - } - else - { - /* FIXME: we probably need to remove an access out, - * if there is already one for the same ID */ - rtsp = RtspClientGet( p_stream, psz_session ); - if( rtsp == NULL ) - { - answer->i_status = 454; - continue; - } - } - - if( httpd_ClientIP( cl, ip ) == NULL ) - { - answer->i_status = 500; - continue; - } - - if( p_sys->i_ttl ) - snprintf( psz_access, sizeof( psz_access ), - "udp{raw,rtcp,ttl=%d}", p_sys->i_ttl ); - else - strcpy( psz_access, "udp{raw,rtcp}" ); - - snprintf( url, sizeof( url ), - ( strchr( ip, ':' ) != NULL ) ? "[%s]:%d" : "%s:%d", - ip, loport ); - - p_access = sout_AccessOutNew( p_stream->p_sout, - psz_access, url ); - if( p_access == NULL ) - { - msg_Err( p_stream, - "cannot create access output for %s://%s", - psz_access, url ); - answer->i_status = 500; - break; - } - - TAB_APPEND( rtsp->i_id, rtsp->id, id ); - TAB_APPEND( rtsp->i_access, rtsp->access, p_access ); - - char *src = var_GetNonEmptyString (p_access, "src-addr"); - int sport = var_GetInteger (p_access, "src-port"); - - httpd_ServerIP( cl, ip ); - - if( ( src != NULL ) && strcmp( src, ip ) ) - { - /* Specify source IP if it is different from the RTSP - * control connection server address */ - char *ptr = strchr( src, '%' ); - if( ptr != NULL ) *ptr = '\0'; /* remove scope ID */ - - httpd_MsgAdd( answer, "Transport", - "RTP/AVP/UDP;unicast;source=%s;" - "client_port=%u-%u;server_port=%u-%u;" - "mode=play", - src, loport, hiport, sport, sport + 1 ); - } - else - { - httpd_MsgAdd( answer, "Transport", - "RTP/AVP/UDP;unicast;" - "client_port=%u-%u;server_port=%u-%u;" - "mode=play", - loport, hiport, sport, sport + 1 ); - } - - answer->i_status = 200; - free( src ); - } - break; - } - break; - } - - default: - answer->i_status = 460; - break; - } - - psz_cseq = httpd_MsgGet( query, "Cseq" ); - if( psz_cseq ) - httpd_MsgAdd( answer, "Cseq", "%s", psz_cseq ); - httpd_MsgAdd( answer, "Server", "%s", PACKAGE_STRING ); - httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); - httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); - - if( psz_session ) - httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session ); - return VLC_SUCCESS; -} - /**************************************************************************** * rtp_packetize_*: ****************************************************************************/ diff --git a/modules/stream_out/rtp.h b/modules/stream_out/rtp.h new file mode 100644 index 0000000000..a91e14ca36 --- /dev/null +++ b/modules/stream_out/rtp.h @@ -0,0 +1,74 @@ +/***************************************************************************** + * rtp.h: rtp stream output module header + ***************************************************************************** + * Copyright (C) 2003-2007 the VideoLAN team + * $Id: rtp.c 21407 2007-08-22 20:10:41Z courmisch $ + * + * Authors: Laurent Aimar + * Rémi Denis-Courmont + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +typedef struct rtsp_client_t rtsp_client_t; + +int RtspSetup( sout_stream_t *p_stream, vlc_url_t * ); + +int RtspCallbackId( httpd_callback_sys_t *, httpd_client_t *, + httpd_message_t *, httpd_message_t * ); + +void RtspClientDel( sout_stream_t *, rtsp_client_t * ); + +char *SDPGenerate( const sout_stream_t *p_stream, + const char *psz_destination, vlc_bool_t b_rtsp ); + +typedef int (*pf_rtp_packetizer_t)( sout_stream_t *, sout_stream_id_t *, + block_t * ); + +struct sout_stream_id_t +{ + sout_stream_t *p_stream; + /* rtp field */ + uint8_t i_payload_type; + uint16_t i_sequence; + uint32_t i_timestamp_start; + uint8_t ssrc[4]; + + /* for sdp */ + int i_clock_rate; + char *psz_rtpmap; + char *psz_fmtp; + char *psz_destination; + int i_port; + int i_cat; + int i_bitrate; + + /* Packetizer specific fields */ + pf_rtp_packetizer_t pf_packetize; + int i_mtu; + + /* for sending the packets */ + sout_access_out_t *p_access; + + vlc_mutex_t lock_rtsp; + int i_rtsp_access; + sout_access_out_t **rtsp_access; + + /* */ + sout_input_t *p_input; + + /* RTSP url control */ + httpd_url_t *p_rtsp_url; +}; diff --git a/modules/stream_out/rtsp.c b/modules/stream_out/rtsp.c new file mode 100644 index 0000000000..03a49d287d --- /dev/null +++ b/modules/stream_out/rtsp.c @@ -0,0 +1,603 @@ +/***************************************************************************** + * rtsp.c: RTSP support for RTP stream output module + ***************************************************************************** + * Copyright (C) 2003-2007 the VideoLAN team + * $Id: rtp.c 21407 2007-08-22 20:10:41Z courmisch $ + * + * Authors: Laurent Aimar + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rtp.h" + +/* For unicast/interleaved streaming */ +struct rtsp_client_t +{ + char *psz_session; + int64_t i_last; /* for timeout */ + + /* is it in "play" state */ + vlc_bool_t b_playing; + + /* output (id-access) */ + int i_id; + sout_stream_id_t **id; + int i_access; + sout_access_out_t **access; +}; + +struct sout_stream_sys_t +{ + /* sdp */ + int64_t i_sdp_id; + int i_sdp_version; + char *psz_sdp; + vlc_mutex_t lock_sdp; + + char *psz_session_name; + char *psz_session_description; + char *psz_session_url; + char *psz_session_email; + + /* */ + vlc_bool_t b_export_sdp_file; + char *psz_sdp_file; + /* sap */ + vlc_bool_t b_export_sap; + session_descriptor_t *p_session; + + httpd_host_t *p_httpd_host; + httpd_file_t *p_httpd_file; + + httpd_host_t *p_rtsp_host; + httpd_url_t *p_rtsp_url; + char *psz_rtsp_control; + char *psz_rtsp_path; + + /* */ + char *psz_destination; + int i_port; + int i_port_audio; + int i_port_video; + int i_ttl; + vlc_bool_t b_latm; + + /* when need to use a private one or when using muxer */ + int i_payload_type; + + /* in case we do TS/PS over rtp */ + sout_mux_t *p_mux; + sout_access_out_t *p_access; + int i_mtu; + sout_access_out_t *p_grab; + uint16_t i_sequence; + uint32_t i_timestamp_start; + uint8_t ssrc[4]; + block_t *packet; + + /* */ + vlc_mutex_t lock_es; + int i_es; + sout_stream_id_t **es; + + /* */ + int i_rtsp; + rtsp_client_t **rtsp; +}; + + +static int RtspCallback( httpd_callback_sys_t *p_args, + httpd_client_t *cl, + httpd_message_t *answer, httpd_message_t *query ); + + + +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_SETUP, 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 rtsp_client_t *RtspClientNew( sout_stream_t *p_stream, const char *psz_session ) +{ + rtsp_client_t *rtsp = malloc( sizeof( rtsp_client_t )); + + rtsp->psz_session = strdup( 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, const 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 ); +} + + +/** Aggregate RTSP callback */ +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; + const char *psz_session = NULL; + const char *psz_cseq = NULL; + + 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; + answer->i_body = 0; + answer->p_body = NULL; + + if( httpd_MsgGet( query, "Require" ) != NULL ) + { + answer->i_status = 551; + httpd_MsgAdd( query, "Unsupported", "%s", + httpd_MsgGet( query, "Require" ) ); + } + else + switch( query->i_type ) + { + case HTTPD_MSG_DESCRIBE: + { + char *psz_sdp = SDPGenerate( p_stream, psz_destination, VLC_TRUE ); + + answer->i_status = 200; + 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_SETUP: + answer->i_status = 459; + break; + + case HTTPD_MSG_PLAY: + { + rtsp_client_t *rtsp; + /* for now only multicast so easy */ + answer->i_status = 200; + + 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: + answer->i_status = 405; + httpd_MsgAdd( answer, "Allow", "DESCRIBE, PLAY, TEARDOWN" ); + break; + + case HTTPD_MSG_TEARDOWN: + { + rtsp_client_t *rtsp; + + /* for now only multicast so easy again */ + answer->i_status = 200; + + 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", "%s", PACKAGE_STRING ); + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + psz_cseq = httpd_MsgGet( query, "Cseq" ); + if( psz_cseq ) + httpd_MsgAdd( answer, "Cseq", "%s", psz_cseq ); + httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + + if( psz_session ) + httpd_MsgAdd( answer, "Session", "%s;timeout=5", psz_session ); + return VLC_SUCCESS; +} + + +/** Finds the next transport choice */ +static inline const char *transport_next( const char *str ) +{ + /* Looks for comma */ + str = strchr( str, ',' ); + if( str == NULL ) + return NULL; /* No more transport options */ + + str++; /* skips comma */ + while( strchr( "\r\n\t ", *str ) ) + str++; + + return (*str) ? str : NULL; +} + + +/** Finds the next transport parameter */ +static inline const char *parameter_next( const char *str ) +{ + while( strchr( ",;", *str ) == NULL ) + str++; + + return (*str == ';') ? (str + 1) : NULL; +} + + +/** Non-aggregate RTSP callback */ +/*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_init[21]; + const char *psz_session; + const char *psz_cseq; + + 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; + answer->i_body = 0; + answer->p_body = NULL; + + /* Create new session ID if needed */ + psz_session = httpd_MsgGet( query, "Session" ); + if( psz_session == NULL ) + { + /* FIXME: should be somewhat secure randomness */ + snprintf( psz_session_init, sizeof(psz_session_init), I64Fd, + NTPtime64() + rand() ); + } + + if( httpd_MsgGet( query, "Require" ) != NULL ) + { + answer->i_status = 551; + httpd_MsgAdd( query, "Unsupported", "%s", + httpd_MsgGet( query, "Require" ) ); + } + else + switch( query->i_type ) + { + case HTTPD_MSG_SETUP: + { + answer->i_status = 461; + + for( const char *tpt = httpd_MsgGet( query, "Transport" ); + tpt != NULL; + tpt = transport_next( tpt ) ) + { + vlc_bool_t b_multicast = VLC_TRUE, b_unsupp = VLC_FALSE; + unsigned loport = 5004, hiport = 5005; /* from RFC3551 */ + + /* Check transport protocol. */ + /* Currently, we only support RTP/AVP over UDP */ + if( strncmp( tpt, "RTP/AVP", 7 ) ) + continue; + tpt += 7; + if( strncmp( tpt, "/UDP", 4 ) == 0 ) + tpt += 4; + if( strchr( ";,", *tpt ) == NULL ) + continue; + + /* Parse transport options */ + for( const char *opt = parameter_next( tpt ); + opt != NULL; + opt = parameter_next( opt ) ) + { + if( strncmp( opt, "multicast", 9 ) == 0) + b_multicast = VLC_TRUE; + else + if( strncmp( opt, "unicast", 7 ) == 0 ) + b_multicast = VLC_FALSE; + else + if( sscanf( opt, "client_port=%u-%u", &loport, &hiport ) == 2 ) + ; + else + if( strncmp( opt, "mode=", 5 ) == 0 ) + { + if( strncasecmp( opt + 5, "play", 4 ) + && strncasecmp( opt + 5, "\"PLAY\"", 6 ) ) + { + /* Not playing?! */ + b_unsupp = VLC_TRUE; + break; + } + } + else + { + /* + * Every other option is unsupported: + * + * "source" and "append" are invalid. + * + * For multicast, "port", "layers", "ttl" are set by the + * stream output configuration. + * + * For unicast, we do not allow "destination" as it + * carries a DoS risk, and we decide on "server_port". + * + * "interleaved" and "ssrc" are not implemented. + */ + b_unsupp = VLC_TRUE; + break; + } + } + + if( b_unsupp ) + continue; + + if( b_multicast ) + { + if( id->psz_destination == NULL ) + continue; + + answer->i_status = 200; + + httpd_MsgAdd( answer, "Transport", + "RTP/AVP/UDP;destination=%s;port=%d-%d;" + "ttl=%d;mode=play", + id->psz_destination, id->i_port, id->i_port+1, + ( p_sys->i_ttl > 0 ) ? p_sys->i_ttl : 1 ); + } + else + { + char ip[NI_MAXNUMERICHOST], psz_access[22], + url[NI_MAXNUMERICHOST + 8]; + sout_access_out_t *p_access; + rtsp_client_t *rtsp = NULL; + + if( ( hiport - loport ) > 1 ) + continue; + + if( psz_session == NULL ) + { + psz_session = psz_session_init; + rtsp = RtspClientNew( p_stream, psz_session ); + } + else + { + /* FIXME: we probably need to remove an access out, + * if there is already one for the same ID */ + rtsp = RtspClientGet( p_stream, psz_session ); + if( rtsp == NULL ) + { + answer->i_status = 454; + continue; + } + } + + if( httpd_ClientIP( cl, ip ) == NULL ) + { + answer->i_status = 500; + continue; + } + + if( p_sys->i_ttl ) + snprintf( psz_access, sizeof( psz_access ), + "udp{raw,rtcp,ttl=%d}", p_sys->i_ttl ); + else + strcpy( psz_access, "udp{raw,rtcp}" ); + + snprintf( url, sizeof( url ), + ( strchr( ip, ':' ) != NULL ) ? "[%s]:%d" : "%s:%d", + ip, loport ); + + p_access = sout_AccessOutNew( p_stream->p_sout, + psz_access, url ); + if( p_access == NULL ) + { + msg_Err( p_stream, + "cannot create access output for %s://%s", + psz_access, url ); + answer->i_status = 500; + break; + } + + TAB_APPEND( rtsp->i_id, rtsp->id, id ); + TAB_APPEND( rtsp->i_access, rtsp->access, p_access ); + + char *src = var_GetNonEmptyString (p_access, "src-addr"); + int sport = var_GetInteger (p_access, "src-port"); + + httpd_ServerIP( cl, ip ); + + if( ( src != NULL ) && strcmp( src, ip ) ) + { + /* Specify source IP if it is different from the RTSP + * control connection server address */ + char *ptr = strchr( src, '%' ); + if( ptr != NULL ) *ptr = '\0'; /* remove scope ID */ + + httpd_MsgAdd( answer, "Transport", + "RTP/AVP/UDP;unicast;source=%s;" + "client_port=%u-%u;server_port=%u-%u;" + "mode=play", + src, loport, hiport, sport, sport + 1 ); + } + else + { + httpd_MsgAdd( answer, "Transport", + "RTP/AVP/UDP;unicast;" + "client_port=%u-%u;server_port=%u-%u;" + "mode=play", + loport, hiport, sport, sport + 1 ); + } + + answer->i_status = 200; + free( src ); + } + break; + } + break; + } + + default: + answer->i_status = 460; + break; + } + + psz_cseq = httpd_MsgGet( query, "Cseq" ); + if( psz_cseq ) + httpd_MsgAdd( answer, "Cseq", "%s", psz_cseq ); + httpd_MsgAdd( answer, "Server", "%s", PACKAGE_STRING ); + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + + if( psz_session ) + httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session ); + return VLC_SUCCESS; +} -- 2.39.2