]> git.sesse.net Git - vlc/blob - modules/access/udp.c
udp: avoid alloc/free on I/O error
[vlc] / modules / access / udp.c
1 /*****************************************************************************
2  * udp.c: raw UDP input module
3  *****************************************************************************
4  * Copyright (C) 2001-2005 VLC authors and VideoLAN
5  * Copyright (C) 2007 Remi Denis-Courmont
6  * $Id$
7  *
8  * Authors: Christophe Massiot <massiot@via.ecp.fr>
9  *          Tristan Leteurtre <tooney@via.ecp.fr>
10  *          Laurent Aimar <fenrir@via.ecp.fr>
11  *          Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
12  *          Remi Denis-Courmont
13  *
14  * Reviewed: 23 October 2003, Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
15  *
16  * This program is free software; you can redistribute it and/or modify it
17  * under the terms of the GNU Lesser General Public License as published by
18  * the Free Software Foundation; either version 2.1 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public License
27  * along with this program; if not, write to the Free Software Foundation,
28  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
29  *****************************************************************************/
30
31 /*****************************************************************************
32  * Preamble
33  *****************************************************************************/
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38
39 #include <errno.h>
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_access.h>
43 #include <vlc_network.h>
44 #include <vlc_block.h>
45
46 #define MTU 65535
47
48 /*****************************************************************************
49  * Module descriptor
50  *****************************************************************************/
51 static int  Open( vlc_object_t * );
52 static void Close( vlc_object_t * );
53
54 #define BUFFER_TEXT N_("Receive buffer")
55 #define BUFFER_LONGTEXT N_("UDP receive buffer size (bytes)" )
56
57 vlc_module_begin ()
58     set_shortname( N_("UDP" ) )
59     set_description( N_("UDP input") )
60     set_category( CAT_INPUT )
61     set_subcategory( SUBCAT_INPUT_ACCESS )
62
63     add_obsolete_integer( "server-port" ) /* since 2.0.0 */
64     add_integer( "udp-buffer", 0x400000, BUFFER_TEXT, BUFFER_LONGTEXT, true )
65
66     set_capability( "access", 0 )
67     add_shortcut( "udp", "udpstream", "udp4", "udp6" )
68
69     set_callbacks( Open, Close )
70 vlc_module_end ()
71
72 struct access_sys_t
73 {
74     int fd;
75     size_t fifo_size;
76     block_fifo_t *fifo;
77     vlc_thread_t thread;
78 };
79
80 /*****************************************************************************
81  * Local prototypes
82  *****************************************************************************/
83 static block_t *BlockUDP( access_t * );
84 static int Control( access_t *, int, va_list );
85 static void* ThreadRead( void *data );
86
87 /*****************************************************************************
88  * Open: open the socket
89  *****************************************************************************/
90 static int Open( vlc_object_t *p_this )
91 {
92     access_t     *p_access = (access_t*)p_this;
93     access_sys_t *sys = malloc( sizeof( *sys ) );
94     if( unlikely( sys == NULL ) )
95         return VLC_ENOMEM;
96
97     p_access->p_sys = sys;
98
99     /* Set up p_access */
100     access_InitFields( p_access );
101     ACCESS_SET_CALLBACKS( NULL, BlockUDP, Control, NULL );
102
103     char *psz_name = strdup( p_access->psz_location );
104     char *psz_parser;
105     const char *psz_server_addr, *psz_bind_addr = "";
106     int  i_bind_port = 1234, i_server_port = 0;
107
108     if( unlikely(psz_name == NULL) )
109         goto error;
110
111     /* Parse psz_name syntax :
112      * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
113     psz_parser = strchr( psz_name, '@' );
114     if( psz_parser != NULL )
115     {
116         /* Found bind address and/or bind port */
117         *psz_parser++ = '\0';
118         psz_bind_addr = psz_parser;
119
120         if( psz_bind_addr[0] == '[' )
121             /* skips bracket'd IPv6 address */
122             psz_parser = strchr( psz_parser, ']' );
123
124         if( psz_parser != NULL )
125         {
126             psz_parser = strchr( psz_parser, ':' );
127             if( psz_parser != NULL )
128             {
129                 *psz_parser++ = '\0';
130                 i_bind_port = atoi( psz_parser );
131             }
132         }
133     }
134
135     psz_server_addr = psz_name;
136     psz_parser = ( psz_server_addr[0] == '[' )
137         ? strchr( psz_name, ']' ) /* skips bracket'd IPv6 address */
138         : psz_name;
139
140     if( psz_parser != NULL )
141     {
142         psz_parser = strchr( psz_parser, ':' );
143         if( psz_parser != NULL )
144         {
145             *psz_parser++ = '\0';
146             i_server_port = atoi( psz_parser );
147         }
148     }
149
150     msg_Dbg( p_access, "opening server=%s:%d local=%s:%d",
151              psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );
152
153     sys->fd = net_OpenDgram( p_access, psz_bind_addr, i_bind_port,
154                              psz_server_addr, i_server_port, IPPROTO_UDP );
155     free( psz_name );
156     if( sys->fd == -1 )
157     {
158         msg_Err( p_access, "cannot open socket" );
159         goto error;
160     }
161
162     sys->fifo = block_FifoNew();
163     if( unlikely( sys->fifo == NULL ) )
164     {
165         net_Close( sys->fd );
166         goto error;
167     }
168
169     sys->fifo_size = var_InheritInteger( p_access, "udp-buffer");
170
171     if( vlc_clone( &sys->thread, ThreadRead, p_access,
172                    VLC_THREAD_PRIORITY_INPUT ) )
173     {
174         block_FifoRelease( sys->fifo );
175         net_Close( sys->fd );
176 error:
177         free( sys );
178         return VLC_EGENERIC;
179     }
180
181     return VLC_SUCCESS;
182 }
183
184 /*****************************************************************************
185  * Close: free unused data structures
186  *****************************************************************************/
187 static void Close( vlc_object_t *p_this )
188 {
189     access_t     *p_access = (access_t*)p_this;
190     access_sys_t *sys = p_access->p_sys;
191
192     vlc_cancel( sys->thread );
193     vlc_join( sys->thread, NULL );
194     block_FifoRelease( sys->fifo );
195     net_Close( sys->fd );
196     free( sys );
197 }
198
199 /*****************************************************************************
200  * Control:
201  *****************************************************************************/
202 static int Control( access_t *p_access, int i_query, va_list args )
203 {
204     bool    *pb_bool;
205     int64_t *pi_64;
206
207     switch( i_query )
208     {
209         case ACCESS_CAN_SEEK:
210         case ACCESS_CAN_FASTSEEK:
211         case ACCESS_CAN_PAUSE:
212         case ACCESS_CAN_CONTROL_PACE:
213             pb_bool = (bool*)va_arg( args, bool* );
214             *pb_bool = false;
215             break;
216
217         case ACCESS_GET_PTS_DELAY:
218             pi_64 = (int64_t*)va_arg( args, int64_t * );
219             *pi_64 = INT64_C(1000)
220                    * var_InheritInteger(p_access, "network-caching");
221             break;
222
223         default:
224             return VLC_EGENERIC;
225     }
226     return VLC_SUCCESS;
227 }
228
229 /*****************************************************************************
230  * BlockUDP:
231  *****************************************************************************/
232 static block_t *BlockUDP( access_t *p_access )
233 {
234     access_sys_t *sys = p_access->p_sys;
235     block_t *block;
236
237     if( p_access->info.b_eof )
238         return NULL;
239
240     block = block_FifoGet( sys->fifo );
241     p_access->info.b_eof = block == NULL;
242     return block;
243 }
244
245 /*****************************************************************************
246  * ThreadRead: Pull packets from socket as soon as possible.
247  *****************************************************************************/
248 static void* ThreadRead( void *data )
249 {
250     access_t *access = data;
251     access_sys_t *sys = access->p_sys;
252
253     for( ;; )
254     {
255         block_t *pkt = block_Alloc(MTU);
256         if (unlikely(pkt == NULL))
257             break;
258
259         ssize_t len;
260
261         block_cleanup_push(pkt);
262         do
263             len = net_Read(access, sys->fd, NULL, pkt->p_buffer, MTU, false);
264         while (len == -1 && errno != EINTR);
265         vlc_cleanup_pop();
266
267         if (len == -1)
268         {
269             block_Release(pkt);
270             break;
271         }
272
273         pkt->i_buffer = len;
274         block_FifoPace(sys->fifo, SIZE_MAX, sys->fifo_size - len);
275         block_FifoPut(sys->fifo, pkt);
276     }
277
278     block_FifoWake( sys->fifo );
279     return NULL;
280 }