Fixed miscellaneous problems with the RTP plug-in.
[vlc.git] / modules / access / rtp.c
1 /*****************************************************************************
2  * rtp.c: RTP access plug-in
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: rtp.c,v 1.4 2002/10/03 21:45:16 massiot Exp $
6  *
7  * Authors: Tristan Leteurtre <tooney@via.ecp.fr>
8  *
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.
13  * 
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <vlc/vlc.h>
35 #include <vlc/input.h>
36
37 #ifdef HAVE_UNISTD_H
38 #   include <unistd.h>
39 #elif defined( _MSC_VER ) && defined( _WIN32 )
40 #   include <io.h>
41 #endif
42
43 #ifdef HAVE_ALLOCA_H
44 #   include <alloca.h>
45 #endif
46
47 #include "network.h"
48
49 #define RTP_HEADER_LEN 12
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54 static int  Open       ( vlc_object_t * );
55 static ssize_t RTPNetworkRead( input_thread_t *, byte_t *, size_t );
56
57 /*****************************************************************************
58  * Module descriptor
59  *****************************************************************************/
60 vlc_module_begin();
61     set_description( _("RTP access module") );
62     set_capability( "access", 0 );
63     add_shortcut( "rtp" );
64     add_shortcut( "rtpstream" );
65     add_shortcut( "rtp4" );
66     add_shortcut( "rtp6" );
67     set_callbacks( Open, __input_FDNetworkClose );
68 vlc_module_end();
69
70 /*****************************************************************************
71  * Open: open the socket
72  *****************************************************************************/
73 static int Open( vlc_object_t *p_this )
74 {
75     input_thread_t *    p_input = (input_thread_t *)p_this;
76     input_socket_t *    p_access_data;
77     module_t *          p_network;
78     char *              psz_network = "";
79     char *              psz_name = strdup(p_input->psz_name);
80     char *              psz_parser = psz_name;
81     char *              psz_server_addr = "";
82     char *              psz_server_port = "";
83     char *              psz_bind_addr = "";
84     char *              psz_bind_port = "";
85     int                 i_bind_port = 0, i_server_port = 0;
86     network_socket_t    socket_desc;
87
88     if( config_GetInt( p_input, "ipv4" ) )
89     {
90         psz_network = "ipv4";
91     }
92     if( config_GetInt( p_input, "ipv6" ) )
93     {
94         psz_network = "ipv6";
95     }
96
97     if( *p_input->psz_access )
98     {
99         /* Find out which shortcut was used */
100         if( !strncmp( p_input->psz_access, "rtp6", 5 ) )
101         {
102             psz_network = "ipv6";
103         }
104         else if( !strncmp( p_input->psz_access, "rtp4", 5 ) )
105         {
106             psz_network = "ipv4";
107         }
108     }
109
110     /* Parse psz_name syntax :
111      * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
112
113     if( *psz_parser && *psz_parser != '@' )
114     {
115         /* Found server */
116         psz_server_addr = psz_parser;
117
118         while( *psz_parser && *psz_parser != ':' && *psz_parser != '@' )
119         {
120             if( *psz_parser == '[' )
121             {
122                 /* IPv6 address */
123                 while( *psz_parser && *psz_parser != ']' )
124                 {
125                     psz_parser++;
126                 }
127             }
128             psz_parser++;
129         }
130
131         if( *psz_parser == ':' )
132         {
133             /* Found server port */
134             *psz_parser = '\0'; /* Terminate server name */
135             psz_parser++;
136             psz_server_port = psz_parser;
137
138             while( *psz_parser && *psz_parser != '@' )
139             {
140                 psz_parser++;
141             }
142         }
143     }
144
145     if( *psz_parser == '@' )
146     {
147         /* Found bind address or bind port */
148         *psz_parser = '\0'; /* Terminate server port or name if necessary */
149         psz_parser++;
150
151         if( *psz_parser && *psz_parser != ':' )
152         {
153             /* Found bind address */
154             psz_bind_addr = psz_parser;
155
156             while( *psz_parser && *psz_parser != ':' )
157             {
158                 if( *psz_parser == '[' )
159                 {
160                     /* IPv6 address */
161                     while( *psz_parser && *psz_parser != ']' )
162                     {
163                         psz_parser++;
164                     }
165                 }
166                 psz_parser++;
167             }
168         }
169
170         if( *psz_parser == ':' )
171         {
172             /* Found bind port */
173             *psz_parser = '\0'; /* Terminate bind address if necessary */
174             psz_parser++;
175
176             psz_bind_port = psz_parser;
177         }
178     }
179
180     /* Convert ports format */
181     if( *psz_server_port )
182     {
183         i_server_port = strtol( psz_server_port, &psz_parser, 10 );
184         if( *psz_parser )
185         {
186             msg_Err( p_input, "cannot parse server port near %s", psz_parser );
187             free(psz_name);
188             return( -1 );
189         }
190     }
191
192     if( *psz_bind_port )
193     {
194         i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
195         if( *psz_parser )
196         {
197             msg_Err( p_input, "cannot parse bind port near %s", psz_parser );
198             free(psz_name);
199             return( -1 );
200         }
201     }
202
203     p_input->pf_read = RTPNetworkRead;
204     p_input->pf_set_program = input_SetProgram;
205     p_input->pf_set_area = NULL;
206     p_input->pf_seek = NULL;
207
208     vlc_mutex_lock( &p_input->stream.stream_lock );
209     p_input->stream.b_pace_control = 0;
210     p_input->stream.b_seekable = 0;
211     p_input->stream.p_selected_area->i_tell = 0;
212     p_input->stream.i_method = INPUT_METHOD_NETWORK;
213     vlc_mutex_unlock( &p_input->stream.stream_lock );
214
215     if( *psz_server_addr || i_server_port )
216     {
217         msg_Err( p_input, "this RTP syntax is deprecated; the server argument will be");
218         msg_Err( p_input, "ignored (%s:%d). If you wanted to enter a multicast address",
219                           psz_server_addr, i_server_port);
220         msg_Err( p_input, "or local port, type : %s:@%s:%d",
221                           *p_input->psz_access ? p_input->psz_access : "udp",
222                           psz_server_addr, i_server_port );
223
224         i_server_port = 0;
225         psz_server_addr = "";
226     }
227  
228     msg_Dbg( p_input, "opening server=%s:%d local=%s:%d",
229              psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );
230
231     /* Prepare the network_socket_t structure */
232     socket_desc.i_type = NETWORK_UDP;
233     socket_desc.psz_bind_addr = psz_bind_addr;
234     socket_desc.i_bind_port = i_bind_port;
235     socket_desc.psz_server_addr = psz_server_addr;
236     socket_desc.i_server_port = i_server_port;
237
238     /* Find an appropriate network module */
239     p_input->p_private = (void*) &socket_desc;
240     p_network = module_Need( p_input, "network", psz_network );
241     free(psz_name);
242     if( p_network == NULL )
243     {
244         return( -1 );
245     }
246     module_Unneed( p_input, p_network );
247     
248     p_access_data = malloc( sizeof(input_socket_t) );
249     p_input->p_access_data = (access_sys_t *)p_access_data;
250
251     if( p_access_data == NULL )
252     {
253         msg_Err( p_input, "out of memory" );
254         return( -1 );
255     }
256
257     p_access_data->i_handle = socket_desc.i_handle;
258     p_input->i_mtu = socket_desc.i_mtu;
259
260     p_input->psz_demux = "ts";
261     
262     return( 0 );
263 }
264
265 /*****************************************************************************
266  * RTPNetworkRead : Read for the network, and parses the RTP header
267  *****************************************************************************/
268 static ssize_t RTPNetworkRead( input_thread_t * p_input, byte_t * p_buffer,
269                            size_t i_len )
270 {
271     int         i_rtp_version;
272     int         i_CSRC_count;
273     int         i_payload_type;
274     
275     byte_t *    p_tmp_buffer = alloca( p_input->i_mtu );
276
277     /* Get the raw data from the socket.
278      * We first assume that RTP header size is the classic RTP_HEADER_LEN. */
279     ssize_t i_ret = input_FDNetworkRead( p_input, p_tmp_buffer,
280                                          p_input->i_mtu );
281
282     if (!i_ret) return 0;
283      
284     /* Parse the header and make some verifications.
285      * See RFC 1889 & RFC 2250. */
286   
287     i_rtp_version  = ( p_tmp_buffer[0] & 0xC0 ) >> 6;
288     i_CSRC_count   = ( p_tmp_buffer[0] & 0x0F );
289     i_payload_type = ( p_tmp_buffer[1] & 0x7F ); 
290   
291     if ( i_rtp_version != 2 ) 
292         msg_Dbg( p_input, "RTP version is %u, should be 2", i_rtp_version );
293   
294     if ( i_payload_type != 33 )
295         msg_Dbg( p_input, "RTP payload type is %u, only 33 (Mpeg2-TS) " \
296                  "is supported", i_payload_type );
297  
298     /* Return the packet without the RTP header. */
299     i_ret -= ( RTP_HEADER_LEN + 4 * i_CSRC_count );
300
301     if ( i_ret > i_len )
302     {
303         /* This should NOT happen. */
304         msg_Warn( p_input, "RTP input trashing %d bytes", i_ret - i_len );
305         i_ret = i_len;
306     }
307
308     p_input->p_vlc->pf_memcpy( p_buffer, 
309                        p_tmp_buffer + RTP_HEADER_LEN + 4 * i_CSRC_count,
310                        i_ret );
311     
312     return i_ret;
313 }