]> git.sesse.net Git - vlc/blob - modules/access/udp.c
* modules/access/udp.c: Merged the UDP and RTP plug-ins, with autodetection of RTP,
[vlc] / modules / access / udp.c
1 /*****************************************************************************
2  * udp.c: raw UDP & RTP access plug-in
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: udp.c,v 1.8 2002/12/30 08:56:19 massiot Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Tristan Leteurtre <tooney@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <fcntl.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/input.h>
37
38 #ifdef HAVE_UNISTD_H
39 #   include <unistd.h>
40 #elif defined( _MSC_VER ) && defined( _WIN32 )
41 #   include <io.h>
42 #endif
43
44 #ifdef WIN32
45 #   include <winsock2.h>
46 #   include <ws2tcpip.h>
47 #   ifndef IN_MULTICAST
48 #       define IN_MULTICAST(a) IN_CLASSD(a)
49 #   endif
50 #else
51 #   include <sys/socket.h>
52 #endif
53
54 #include "network.h"
55
56 #define RTP_HEADER_LEN 12
57
58 /*****************************************************************************
59  * Local prototypes
60  *****************************************************************************/
61 static int  Open       ( vlc_object_t * );
62 static void Close      ( vlc_object_t * );
63 static ssize_t Read    ( input_thread_t *, byte_t *, size_t );
64 static ssize_t RTPRead ( input_thread_t *, byte_t *, size_t );
65 static ssize_t RTPChoose( input_thread_t *, byte_t *, size_t );
66
67 /*****************************************************************************
68  * Module descriptor
69  *****************************************************************************/
70 #define CACHING_TEXT N_("caching value in ms")
71 #define CACHING_LONGTEXT N_( \
72     "Allows you to modify the default caching value for udp streams. This " \
73     "value should be set in miliseconds units." )
74
75 vlc_module_begin();
76     set_description( _("raw UDP access module") );
77     add_category_hint( N_("udp"), NULL );
78     add_integer( "udp-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT );
79     set_capability( "access", 0 );
80     add_shortcut( "udp" );
81     add_shortcut( "udpstream" );
82     add_shortcut( "udp4" );
83     add_shortcut( "udp6" );
84     set_callbacks( Open, Close );
85 vlc_module_end();
86
87 /*****************************************************************************
88  * Open: open the socket
89  *****************************************************************************/
90 static int Open( vlc_object_t *p_this )
91 {
92     input_thread_t *    p_input = (input_thread_t *)p_this;
93     input_socket_t *    p_access_data;
94     module_t *          p_network;
95     char *              psz_network = "";
96     char *              psz_name = strdup(p_input->psz_name);
97     char *              psz_parser = psz_name;
98     char *              psz_server_addr = "";
99     char *              psz_server_port = "";
100     char *              psz_bind_addr = "";
101     char *              psz_bind_port = "";
102     int                 i_bind_port = 0, i_server_port = 0;
103     network_socket_t    socket_desc;
104
105     if( config_GetInt( p_input, "ipv4" ) )
106     {
107         psz_network = "ipv4";
108     }
109     if( config_GetInt( p_input, "ipv6" ) )
110     {
111         psz_network = "ipv6";
112     }
113
114     if( *p_input->psz_access )
115     {
116         /* Find out which shortcut was used */
117         if( !strncmp( p_input->psz_access, "udp6", 5 ) )
118         {
119             psz_network = "ipv6";
120         }
121         else if( !strncmp( p_input->psz_access, "udp4", 5 ) )
122         {
123             psz_network = "ipv4";
124         }
125     }
126
127     /* Parse psz_name syntax :
128      * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
129
130     if( *psz_parser && *psz_parser != '@' )
131     {
132         /* Found server */
133         psz_server_addr = psz_parser;
134
135         while( *psz_parser && *psz_parser != ':' && *psz_parser != '@' )
136         {
137             if( *psz_parser == '[' )
138             {
139                 /* IPv6 address */
140                 while( *psz_parser && *psz_parser != ']' )
141                 {
142                     psz_parser++;
143                 }
144             }
145             psz_parser++;
146         }
147
148         if( *psz_parser == ':' )
149         {
150             /* Found server port */
151             *psz_parser = '\0'; /* Terminate server name */
152             psz_parser++;
153             psz_server_port = psz_parser;
154
155             while( *psz_parser && *psz_parser != '@' )
156             {
157                 psz_parser++;
158             }
159         }
160     }
161
162     if( *psz_parser == '@' )
163     {
164         /* Found bind address or bind port */
165         *psz_parser = '\0'; /* Terminate server port or name if necessary */
166         psz_parser++;
167
168         if( *psz_parser && *psz_parser != ':' )
169         {
170             /* Found bind address */
171             psz_bind_addr = psz_parser;
172
173             while( *psz_parser && *psz_parser != ':' )
174             {
175                 if( *psz_parser == '[' )
176                 {
177                     /* IPv6 address */
178                     while( *psz_parser && *psz_parser != ']' )
179                     {
180                         psz_parser++;
181                     }
182                 }
183                 psz_parser++;
184             }
185         }
186
187         if( *psz_parser == ':' )
188         {
189             /* Found bind port */
190             *psz_parser = '\0'; /* Terminate bind address if necessary */
191             psz_parser++;
192
193             psz_bind_port = psz_parser;
194         }
195     }
196
197     /* Convert ports format */
198     if( *psz_server_port )
199     {
200         i_server_port = strtol( psz_server_port, &psz_parser, 10 );
201         if( *psz_parser )
202         {
203             msg_Err( p_input, "cannot parse server port near %s", psz_parser );
204             free(psz_name);
205             return( -1 );
206         }
207     }
208
209     if( *psz_bind_port )
210     {
211         i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
212         if( *psz_parser )
213         {
214             msg_Err( p_input, "cannot parse bind port near %s", psz_parser );
215             free(psz_name);
216             return( -1 );
217         }
218     }
219
220     if( i_bind_port == 0 )
221     {
222         i_bind_port = config_GetInt( p_this, "server-port" );
223     }
224
225     p_input->pf_read = RTPChoose;
226     p_input->pf_set_program = input_SetProgram;
227     p_input->pf_set_area = NULL;
228     p_input->pf_seek = NULL;
229
230     vlc_mutex_lock( &p_input->stream.stream_lock );
231     p_input->stream.b_pace_control = 0;
232     p_input->stream.b_seekable = 0;
233     p_input->stream.p_selected_area->i_tell = 0;
234     p_input->stream.i_method = INPUT_METHOD_NETWORK;
235     vlc_mutex_unlock( &p_input->stream.stream_lock );
236
237     if( *psz_server_addr || i_server_port )
238     {
239         msg_Err( p_input, "this UDP syntax is deprecated; the server argument will be");
240         msg_Err( p_input, "ignored (%s:%d). If you wanted to enter a multicast address",
241                           psz_server_addr, i_server_port);
242         msg_Err( p_input, "or local port, type : %s:@%s:%d",
243                           *p_input->psz_access ? p_input->psz_access : "udp",
244                           psz_server_addr, i_server_port );
245
246         i_server_port = 0;
247         psz_server_addr = "";
248     }
249  
250     msg_Dbg( p_input, "opening server=%s:%d local=%s:%d",
251              psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );
252
253     /* Prepare the network_socket_t structure */
254     socket_desc.i_type = NETWORK_UDP;
255     socket_desc.psz_bind_addr = psz_bind_addr;
256     socket_desc.i_bind_port = i_bind_port;
257     socket_desc.psz_server_addr = psz_server_addr;
258     socket_desc.i_server_port = i_server_port;
259
260     /* Find an appropriate network module */
261     p_input->p_private = (void*) &socket_desc;
262     p_network = module_Need( p_input, "network", psz_network );
263     free(psz_name);
264     if( p_network == NULL )
265     {
266         return( -1 );
267     }
268     module_Unneed( p_input, p_network );
269     
270     p_access_data = malloc( sizeof(input_socket_t) );
271     p_input->p_access_data = (access_sys_t *)p_access_data;
272
273     if( p_access_data == NULL )
274     {
275         msg_Err( p_input, "out of memory" );
276         return( -1 );
277     }
278
279     p_access_data->i_handle = socket_desc.i_handle;
280     p_input->i_mtu = socket_desc.i_mtu;
281
282     /* Update default_pts to a suitable value for udp access */
283     p_input->i_pts_delay = config_GetInt( p_input, "udp-caching" ) * 1000;
284
285     return( 0 );
286 }
287
288 /*****************************************************************************
289  * Close: free unused data structures
290  *****************************************************************************/
291 static void Close( vlc_object_t *p_this )
292 {
293     input_thread_t *  p_input = (input_thread_t *)p_this;
294     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
295
296     msg_Info( p_input, "closing UDP target `%s'", p_input->psz_source );
297
298 #ifdef UNDER_CE
299     CloseHandle( (HANDLE)p_access_data->i_handle );
300 #elif defined( WIN32 )
301     closesocket( p_access_data->i_handle );
302 #else
303     close( p_access_data->i_handle );
304 #endif
305
306     free( p_access_data );
307 }
308
309 /*****************************************************************************
310  * Read: read on a file descriptor, checking b_die periodically
311  *****************************************************************************/
312 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
313 {
314 #ifdef UNDER_CE
315     return -1;
316
317 #else
318     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
319     struct timeval  timeout;
320     fd_set          fds;
321     int             i_ret;
322
323     /* Initialize file descriptor set */
324     FD_ZERO( &fds );
325     FD_SET( p_access_data->i_handle, &fds );
326
327     /* We'll wait 0.5 second if nothing happens */
328     timeout.tv_sec = 0;
329     timeout.tv_usec = 500000;
330
331     /* Find if some data is available */
332     i_ret = select( p_access_data->i_handle + 1, &fds,
333                     NULL, NULL, &timeout );
334
335     if( i_ret == -1 && errno != EINTR )
336     {
337         msg_Err( p_input, "network select error (%s)", strerror(errno) );
338     }
339     else if( i_ret > 0 )
340     {
341         ssize_t i_recv = recv( p_access_data->i_handle, p_buffer, i_len, 0 );
342
343         if( i_recv < 0 )
344         {
345             msg_Err( p_input, "recv failed (%s)", strerror(errno) );
346         }
347
348         return i_recv;
349     }
350
351     return 0;
352
353 #endif
354 }
355
356 /*****************************************************************************
357  * RTPRead : read from the network, and parse the RTP header
358  *****************************************************************************/
359 static ssize_t RTPRead( input_thread_t * p_input, byte_t * p_buffer,
360                         size_t i_len )
361 {
362     int         i_rtp_version;
363     int         i_CSRC_count;
364     int         i_payload_type;
365
366     byte_t *    p_tmp_buffer = alloca( p_input->i_mtu );
367
368     /* Get the raw data from the socket.
369      * We first assume that RTP header size is the classic RTP_HEADER_LEN. */
370     ssize_t i_ret = Read( p_input, p_tmp_buffer, p_input->i_mtu );
371
372     if ( !i_ret ) return 0;
373
374     /* Parse the header and make some verifications.
375      * See RFC 1889 & RFC 2250. */
376
377     i_rtp_version  = ( p_tmp_buffer[0] & 0xC0 ) >> 6;
378     i_CSRC_count   = ( p_tmp_buffer[0] & 0x0F );
379     i_payload_type = ( p_tmp_buffer[1] & 0x7F );
380
381     if ( i_rtp_version != 2 )
382         msg_Dbg( p_input, "RTP version is %u, should be 2", i_rtp_version );
383
384     if ( i_payload_type != 33 && i_payload_type != 14
385           && i_payload_type != 32 )
386         msg_Dbg( p_input, "unsupported RTP payload type (%u)", i_payload_type );
387
388     /* Return the packet without the RTP header. */
389     i_ret -= ( RTP_HEADER_LEN + 4 * i_CSRC_count );
390
391     if ( (size_t)i_ret > i_len )
392     {
393         /* This should NOT happen. */
394         msg_Warn( p_input, "RTP input trashing %d bytes", i_ret - i_len );
395         i_ret = i_len;
396     }
397
398     p_input->p_vlc->pf_memcpy( p_buffer,
399                        p_tmp_buffer + RTP_HEADER_LEN + 4 * i_CSRC_count,
400                        i_ret );
401
402     return i_ret;
403 }
404
405 /*****************************************************************************
406  * RTPChoose : read from the network, and decide whether it's UDP or RTP
407  *****************************************************************************/
408 static ssize_t RTPChoose( input_thread_t * p_input, byte_t * p_buffer,
409                           size_t i_len )
410 {
411     int         i_rtp_version;
412     int         i_CSRC_count;
413     int         i_payload_type;
414
415     byte_t *    p_tmp_buffer = alloca( p_input->i_mtu );
416
417     /* Get the raw data from the socket.
418      * We first assume that RTP header size is the classic RTP_HEADER_LEN. */
419     ssize_t i_ret = Read( p_input, p_tmp_buffer, p_input->i_mtu );
420
421     if ( !i_ret ) return 0;
422     
423     /* Check that it's not TS. */
424     if ( p_tmp_buffer[0] == 0x47 )
425     {
426         msg_Dbg( p_input, "detected TS over raw UDP" );
427         p_input->pf_read = Read;
428         p_input->p_vlc->pf_memcpy( p_buffer, p_tmp_buffer, i_ret );
429         return i_ret;
430     }
431
432     /* Parse the header and make some verifications.
433      * See RFC 1889 & RFC 2250. */
434
435     i_rtp_version  = ( p_tmp_buffer[0] & 0xC0 ) >> 6;
436     i_CSRC_count   = ( p_tmp_buffer[0] & 0x0F );
437     i_payload_type = ( p_tmp_buffer[1] & 0x7F );
438
439     if ( i_rtp_version != 2 )
440     {
441         msg_Dbg( p_input, "no RTP header detected" );
442         p_input->pf_read = Read;
443         p_input->p_vlc->pf_memcpy( p_buffer, p_tmp_buffer, i_ret );
444         return i_ret;
445     }
446
447     switch ( i_payload_type )
448     {
449     case 33:
450         msg_Dbg( p_input, "detected TS over RTP" );
451         break;
452
453     case 14:
454         msg_Dbg( p_input, "detected MPEG audio over RTP" );
455         break;
456
457     case 32:
458         msg_Dbg( p_input, "detected MPEG video over RTP" );
459         break;
460
461     default:
462         msg_Dbg( p_input, "no RTP header detected" );
463         p_input->pf_read = Read;
464         p_input->p_vlc->pf_memcpy( p_buffer, p_tmp_buffer, i_ret );
465         return i_ret;
466     }
467
468     /* Return the packet without the RTP header. */
469     p_input->pf_read = RTPRead;
470     i_ret -= ( RTP_HEADER_LEN + 4 * i_CSRC_count );
471
472     if ( (size_t)i_ret > i_len )
473     {
474         /* This should NOT happen. */
475         msg_Warn( p_input, "RTP input trashing %d bytes", i_ret - i_len );
476         i_ret = i_len;
477     }
478
479     p_input->p_vlc->pf_memcpy( p_buffer,
480                        p_tmp_buffer + RTP_HEADER_LEN + 4 * i_CSRC_count,
481                        i_ret );
482
483     return i_ret;
484 }