]> git.sesse.net Git - vlc/blob - modules/access/udp.c
0a233173d4599c09b806221d32a4635f9398b86c
[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     bool running;
76     size_t fifo_size;
77     block_fifo_t *fifo;
78     vlc_thread_t thread;
79 };
80
81 /*****************************************************************************
82  * Local prototypes
83  *****************************************************************************/
84 static block_t *BlockUDP( access_t * );
85 static int Control( access_t *, int, va_list );
86 static void* ThreadRead( void *data );
87
88 /*****************************************************************************
89  * Open: open the socket
90  *****************************************************************************/
91 static int Open( vlc_object_t *p_this )
92 {
93     access_t     *p_access = (access_t*)p_this;
94     access_sys_t *sys = malloc( sizeof( *sys ) );
95     if( unlikely( sys == NULL ) )
96         return VLC_ENOMEM;
97
98     p_access->p_sys = sys;
99
100     /* Set up p_access */
101     access_InitFields( p_access );
102     ACCESS_SET_CALLBACKS( NULL, BlockUDP, Control, NULL );
103
104     char *psz_name = strdup( p_access->psz_location );
105     char *psz_parser;
106     const char *psz_server_addr, *psz_bind_addr = "";
107     int  i_bind_port = 1234, i_server_port = 0;
108
109     if( unlikely(psz_name == NULL) )
110         goto error;
111
112     /* Parse psz_name syntax :
113      * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
114     psz_parser = strchr( psz_name, '@' );
115     if( psz_parser != NULL )
116     {
117         /* Found bind address and/or bind port */
118         *psz_parser++ = '\0';
119         psz_bind_addr = psz_parser;
120
121         if( psz_bind_addr[0] == '[' )
122             /* skips bracket'd IPv6 address */
123             psz_parser = strchr( psz_parser, ']' );
124
125         if( psz_parser != NULL )
126         {
127             psz_parser = strchr( psz_parser, ':' );
128             if( psz_parser != NULL )
129             {
130                 *psz_parser++ = '\0';
131                 i_bind_port = atoi( psz_parser );
132             }
133         }
134     }
135
136     psz_server_addr = psz_name;
137     psz_parser = ( psz_server_addr[0] == '[' )
138         ? strchr( psz_name, ']' ) /* skips bracket'd IPv6 address */
139         : psz_name;
140
141     if( psz_parser != NULL )
142     {
143         psz_parser = strchr( psz_parser, ':' );
144         if( psz_parser != NULL )
145         {
146             *psz_parser++ = '\0';
147             i_server_port = atoi( psz_parser );
148         }
149     }
150
151     msg_Dbg( p_access, "opening server=%s:%d local=%s:%d",
152              psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );
153
154     sys->fd = net_OpenDgram( p_access, psz_bind_addr, i_bind_port,
155                              psz_server_addr, i_server_port, IPPROTO_UDP );
156     free( psz_name );
157     if( sys->fd == -1 )
158     {
159         msg_Err( p_access, "cannot open socket" );
160         goto error;
161     }
162
163     sys->fifo = block_FifoNew();
164     if( unlikely( sys->fifo == NULL ) )
165     {
166         net_Close( sys->fd );
167         goto error;
168     }
169
170     sys->running = true;
171     sys->fifo_size = var_InheritInteger( p_access, "udp-buffer");
172
173     if( vlc_clone( &sys->thread, ThreadRead, p_access,
174                    VLC_THREAD_PRIORITY_INPUT ) )
175     {
176         block_FifoRelease( sys->fifo );
177         net_Close( sys->fd );
178 error:
179         free( sys );
180         return VLC_EGENERIC;
181     }
182
183     return VLC_SUCCESS;
184 }
185
186 /*****************************************************************************
187  * Close: free unused data structures
188  *****************************************************************************/
189 static void Close( vlc_object_t *p_this )
190 {
191     access_t     *p_access = (access_t*)p_this;
192     access_sys_t *sys = p_access->p_sys;
193
194     vlc_cancel( sys->thread );
195     vlc_join( sys->thread, NULL );
196     block_FifoRelease( sys->fifo );
197     net_Close( sys->fd );
198     free( sys );
199 }
200
201 /*****************************************************************************
202  * Control:
203  *****************************************************************************/
204 static int Control( access_t *p_access, int i_query, va_list args )
205 {
206     bool    *pb_bool;
207     int64_t *pi_64;
208
209     switch( i_query )
210     {
211         case ACCESS_CAN_SEEK:
212         case ACCESS_CAN_FASTSEEK:
213         case ACCESS_CAN_PAUSE:
214         case ACCESS_CAN_CONTROL_PACE:
215             pb_bool = (bool*)va_arg( args, bool* );
216             *pb_bool = false;
217             break;
218
219         case ACCESS_GET_PTS_DELAY:
220             pi_64 = (int64_t*)va_arg( args, int64_t * );
221             *pi_64 = INT64_C(1000)
222                    * var_InheritInteger(p_access, "network-caching");
223             break;
224
225         default:
226             return VLC_EGENERIC;
227     }
228     return VLC_SUCCESS;
229 }
230
231 /*****************************************************************************
232  * BlockUDP:
233  *****************************************************************************/
234 static block_t *BlockUDP( access_t *p_access )
235 {
236     access_sys_t *sys = p_access->p_sys;
237     block_t *block;
238
239     if (p_access->info.b_eof)
240         return NULL;
241
242     vlc_fifo_Lock(sys->fifo);
243     while (vlc_fifo_IsEmpty(sys->fifo) && sys->running)
244        vlc_fifo_Wait(sys->fifo);
245
246     block = vlc_fifo_DequeueUnlocked(sys->fifo);
247     p_access->info.b_eof = !sys->running;
248     vlc_fifo_Unlock(sys->fifo);
249
250     return block;
251 }
252
253 /*****************************************************************************
254  * ThreadRead: Pull packets from socket as soon as possible.
255  *****************************************************************************/
256 static void* ThreadRead( void *data )
257 {
258     access_t *access = data;
259     access_sys_t *sys = access->p_sys;
260
261     vlc_fifo_Lock(sys->fifo);
262     vlc_fifo_CleanupPush(sys->fifo);
263
264     for(;;)
265     {
266         block_t *pkt = block_Alloc(MTU);
267         if (unlikely(pkt == NULL))
268             break;
269
270         ssize_t len;
271
272         block_cleanup_push(pkt);
273         do
274             len = net_Read(access, sys->fd, NULL, pkt->p_buffer, MTU, false);
275         while (len == -1 && errno != EINTR);
276         vlc_cleanup_pop();
277
278         if (len == -1)
279         {
280             block_Release(pkt);
281             break;
282         }
283
284         pkt->i_buffer = len;
285
286         /* Discard old buffers on overflow */
287         while (vlc_fifo_GetBytes(sys->fifo) + len > sys->fifo_size)
288             block_Release(vlc_fifo_DequeueUnlocked(sys->fifo));
289
290         vlc_fifo_QueueUnlocked(sys->fifo, pkt);
291     }
292
293     sys->running = false;
294     vlc_fifo_Signal(sys->fifo);
295     vlc_cleanup_run();
296
297     return NULL;
298 }