1 /*****************************************************************************
2 * rtsp.c: Retreive a description from an RTSP url.
3 *****************************************************************************
4 * Copyright (C) 2003 VideoLAN
5 * $Id: rtsp.c,v 1.2 2003/08/03 12:04:28 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
29 #include <vlc/input.h>
38 # include <winsock2.h>
39 # include <ws2tcpip.h>
41 # define IN_MULTICAST(a) IN_CLASSD(a)
44 # include <sys/socket.h>
45 # include <netinet/in.h>
47 # include <arpa/inet.h>
48 # elif defined( SYS_BEOS )
49 # include <net/netdb.h>
57 * - login/password management
61 /*****************************************************************************
63 *****************************************************************************/
64 static int Open ( vlc_object_t * );
65 static void Close ( vlc_object_t * );
68 set_description( _("RTSP SDP request") );
69 set_capability( "access", 0 );
70 add_category_hint( "stream", NULL, VLC_FALSE );
71 add_integer( "rtsp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
73 add_shortcut( "rtsp" );
74 add_shortcut( "rtspu" );
75 set_callbacks( Open, Close );
78 /*****************************************************************************
80 *****************************************************************************/
81 static ssize_t Read ( input_thread_t *, byte_t *, size_t );
92 static void url_Parse( url_t *, char * );
93 static void url_Clean( url_t * );
95 static int NetRead ( input_thread_t *, int , uint8_t *, int );
96 static void NetClose( input_thread_t *p_input, int i_handle );
108 #define RTSP_DESCRIBE_REQUEST \
109 "DESCRIBE rtsp://%s:%d/%s RTSP/1.0\r\n" \
111 "User-Agent: " COPYRIGHT_MESSAGE "\r\n" \
112 "Accept: application/sdp\r\n" \
115 /*****************************************************************************
116 * Open: open the file
117 *****************************************************************************/
118 static int Open( vlc_object_t *p_this )
120 input_thread_t *p_input = (input_thread_t *)p_this;
124 char *psz_network = "";
125 vlc_bool_t b_udp = VLC_FALSE;
129 network_socket_t socket_desc;
131 uint8_t *p_request, *p;
137 /* Parse the URL [user:password@]host[:port]/path */
138 url_Parse( &url, p_input->psz_name );
139 if( url.i_port <= 0 )
141 /* Default RTSP port */
146 var_Create( p_input, "ipv4", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
147 var_Get( p_input, "ipv4", &val );
150 psz_network = "ipv4";
153 var_Create( p_input, "ipv6", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
154 var_Get( p_input, "ipv6", &val );
157 psz_network = "ipv6";
161 if( !strcmp( p_input->psz_access, "rtspu" ) )
168 msg_Err( p_input, "RTSP over UDP not supported" );
172 /* Open the TCP connection */
173 socket_desc.i_type = NETWORK_TCP;
174 socket_desc.psz_server_addr = url.psz_host;
175 socket_desc.i_server_port = url.i_port;
176 socket_desc.psz_bind_addr = "";
177 socket_desc.i_bind_port = 0;
178 socket_desc.i_ttl = 0;
180 p_input->p_private = (void*)&socket_desc;
181 p_network = module_Need( p_input, "network", psz_network );
182 if( p_network == NULL )
186 module_Unneed( p_input, p_network );
187 i_handle = socket_desc.i_handle;
188 p_input->i_mtu = socket_desc.i_mtu;
190 msg_Dbg( p_input, "connected with %s:%d", url.psz_host, url.i_port );
193 i_request = strlen( RTSP_DESCRIBE_REQUEST ) +
194 strlen( url.psz_host ) + 5 + strlen( url.psz_path ) + 1;
195 p_request = malloc( i_request );
196 sprintf( p_request, RTSP_DESCRIBE_REQUEST,
197 url.psz_host, url.i_port, url.psz_path );
199 msg_Dbg( p_input, "Request=%s", p_request );
200 /* Send the request */
201 for( p = p_request; p < p_request + strlen( p_request ); )
205 i_send = send( i_handle, p, strlen( p ), 0 );
215 /* Read the complete answer*/
217 p_request = p = malloc( i_request );
223 i_recv = NetRead( p_input, i_handle, p, i_request - ( p - p_request ) - 1);
233 if( p >= p_request + i_request - 1 )
235 int i_size = p - p_request;
237 p_request = realloc( p_request, i_request + 1024 );
239 p = p_request + i_size;
243 if( strlen( p_request ) <= 0 ||
244 ( !strstr( p_request, "\n\n" ) && !strstr( p_request, "\r\n\r\n" ) ) )
246 msg_Dbg( p_input, "failed to retreive description" );
250 /* Parse the header */
251 msg_Dbg( p_input, "answer header=%s", p_request );
252 if( strncmp( p_request, "RTSP/1.", strlen( "RTSP/1." ) ) )
254 msg_Err( p_input, "invalid answer" );
258 i_code = atoi( &p_request[strlen( "RTSP/1.x" )] );
260 if( i_code / 100 != 2 )
262 msg_Err( p_input, "return code is %d (!=2xx), not yet supported", i_code );
267 p_input->p_access_data = p_sys = malloc( sizeof( access_sys_t ) );
268 p_sys->i_handle = i_handle;
269 p_sys->p_data = p_request;
270 p_sys->i_data = strlen( p_request );
271 if( ( p = strstr( p_request, "\r\n\r\n" ) ) )
273 p_sys->p_actu = p + 4;
275 else if( ( p = strstr( p_request, "\n\n" ) ) )
277 p_sys->p_actu = p + 2;
281 msg_Err( p_input, "mhhh ? cannot happens" );
285 /* Set exported functions */
286 p_input->pf_read = Read;
287 p_input->pf_seek = NULL;
288 p_input->pf_set_program = input_SetProgram;
289 p_input->pf_set_area = NULL;
290 p_input->p_private = NULL;
292 /* Finished to set some variable */
293 vlc_mutex_lock( &p_input->stream.stream_lock );
294 p_input->stream.b_pace_control = VLC_TRUE;
295 p_input->stream.p_selected_area->i_tell = 0;
296 p_input->stream.b_seekable = 0;
297 p_input->stream.p_selected_area->i_size = 0;
298 p_input->stream.i_method = INPUT_METHOD_NETWORK;
299 vlc_mutex_unlock( &p_input->stream.stream_lock );
301 /* Update default_pts to a suitable value for RTSP access */
302 p_input->i_pts_delay = config_GetInt( p_input, "rtsp-caching" ) * 1000;
304 NetClose( p_input, i_handle );
311 NetClose( p_input, i_handle );
317 /*****************************************************************************
318 * Close: close the target
319 *****************************************************************************/
320 static void Close( vlc_object_t * p_this )
322 input_thread_t *p_input = (input_thread_t *)p_this;
323 access_sys_t *p_sys = p_input->p_access_data;
325 free( p_sys->p_data );
329 /*****************************************************************************
330 * Read: standard read on a file descriptor.
331 *****************************************************************************/
332 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
334 access_sys_t *p_sys = p_input->p_access_data;
336 int i_copy = __MIN( (int)i_len, &p_sys->p_data[p_sys->i_data] - p_sys->p_actu );
343 memcpy( p_buffer, p_sys->p_actu, i_copy );
345 p_sys->p_actu += i_copy;
350 /*****************************************************************************
352 *****************************************************************************/
353 static void url_Parse( url_t *url, char *psz_url )
355 char *psz_dup = strdup( psz_url );
364 if( strchr( p, '@' ) )
366 /* TODO: There is a login/password */
368 url->psz_login = strdup( "" );
369 url->psz_password = strdup( "" );
373 while( *p && *p != ':' && *p != '/' )
378 while( *p && *p != ']' )
393 while( *p && *p != '/' )
397 url->i_port = atoi( psz_port );
405 url->psz_host = strdup( psz_tmp );
408 url->psz_path = strdup( p );
413 static void url_Clean( url_t *url )
417 free( url->psz_host );
421 free( url->psz_path );
425 free( url->psz_login );
427 if( url->psz_password )
429 free( url->psz_password );
435 static int NetRead( input_thread_t *p_input,
437 uint8_t *p_buffer, int i_len )
439 struct timeval timeout;
444 /* Initialize file descriptor set */
446 FD_SET( i_handle, &fds );
448 /* We'll wait 1 second if nothing happens */
452 /* Find if some data is available */
453 while( (i_ret = select( i_handle + 1, &fds,
454 NULL, NULL, &timeout )) == 0
455 || (i_ret < 0 && errno == EINTR) )
458 FD_SET( i_handle, &fds );
462 if( p_input->b_die || p_input->b_error )
470 msg_Err( p_input, "network select error (%s)", strerror(errno) );
474 i_recv = recv( i_handle, p_buffer, i_len, 0 );
478 msg_Err( p_input, "recv failed (%s)", strerror(errno) );
486 static void NetClose( input_thread_t *p_input, int i_handle )
488 #if defined( UNDER_CE )
489 CloseHandle( (HANDLE)i_handle );
490 #elif defined( WIN32 )
491 closesocket( i_handle );