srt: fix build issue with recent SRT library
[vlc.git] / modules / access_output / udp.c
1 /*****************************************************************************
2  * udp.c
3  *****************************************************************************
4  * Copyright (C) 2001-2007 VLC authors and VideoLAN
5  *
6  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7  *          Eric Petit <titer@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <assert.h>
37 #include <errno.h>
38
39 #include <vlc_queue.h>
40 #include <vlc_sout.h>
41 #include <vlc_block.h>
42
43 #ifdef _WIN32
44 #   include <winsock2.h>
45 #   include <ws2tcpip.h>
46 #elif defined (HAVE_SYS_SOCKET_H)
47 #   include <sys/socket.h>
48 #endif
49
50 #include <vlc_network.h>
51
52 #define MAX_EMPTY_BLOCKS 200
53
54 /*****************************************************************************
55  * Module descriptor
56  *****************************************************************************/
57 static int  Open ( vlc_object_t * );
58 static void Close( vlc_object_t * );
59
60 #define SOUT_CFG_PREFIX "sout-udp-"
61
62 #define CACHING_TEXT N_("Caching value (ms)")
63 #define CACHING_LONGTEXT N_( \
64     "Default caching value for outbound UDP streams. This " \
65     "value should be set in milliseconds." )
66
67 #define GROUP_TEXT N_("Group packets")
68 #define GROUP_LONGTEXT N_("Packets can be sent one by one at the right time " \
69                           "or by groups. You can choose the number " \
70                           "of packets that will be sent at a time. It " \
71                           "helps reducing the scheduling load on " \
72                           "heavily-loaded systems." )
73
74 vlc_module_begin ()
75     set_description( N_("UDP stream output") )
76     set_shortname( "UDP" )
77     set_category( CAT_SOUT )
78     set_subcategory( SUBCAT_SOUT_ACO )
79     add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, CACHING_TEXT, CACHING_LONGTEXT, true )
80     add_integer( SOUT_CFG_PREFIX "group", 1, GROUP_TEXT, GROUP_LONGTEXT,
81                                  true )
82
83     set_capability( "sout access", 0 )
84     add_shortcut( "udp" )
85     set_callbacks( Open, Close )
86 vlc_module_end ()
87
88 /*****************************************************************************
89  * Exported prototypes
90  *****************************************************************************/
91
92 static const char *const ppsz_sout_options[] = {
93     "caching",
94     "group",
95     NULL
96 };
97
98 /* Options handled by the libvlc network core */
99 static const char *const ppsz_core_options[] = {
100     "dscp",
101     "ttl",
102     "miface",
103     NULL
104 };
105
106 static ssize_t Write   ( sout_access_out_t *, block_t * );
107 static int Control( sout_access_out_t *, int, va_list );
108
109 static void* ThreadWrite( void * );
110
111 typedef struct
112 {
113     vlc_tick_t    i_caching;
114     int           i_handle;
115     bool          b_mtu_warning;
116     bool          dead;
117     size_t        i_mtu;
118
119     vlc_queue_t   queue;
120     block_t      *p_buffer;
121
122     vlc_thread_t  thread;
123 } sout_access_out_sys_t;
124
125 #define DEFAULT_PORT 1234
126
127 /*****************************************************************************
128  * Open: open the file
129  *****************************************************************************/
130 static int Open( vlc_object_t *p_this )
131 {
132     sout_access_out_t       *p_access = (sout_access_out_t*)p_this;
133     sout_access_out_sys_t   *p_sys;
134
135     char                *psz_dst_addr = NULL;
136     int                 i_dst_port;
137
138     int                 i_handle;
139
140     config_ChainParse( p_access, SOUT_CFG_PREFIX,
141                        ppsz_sout_options, p_access->p_cfg );
142     config_ChainParse( p_access, "",
143                        ppsz_core_options, p_access->p_cfg );
144
145     if (var_Create (p_access, "dst-port", VLC_VAR_INTEGER)
146      || var_Create (p_access, "src-port", VLC_VAR_INTEGER)
147      || var_Create (p_access, "dst-addr", VLC_VAR_STRING)
148      || var_Create (p_access, "src-addr", VLC_VAR_STRING))
149     {
150         return VLC_ENOMEM;
151     }
152
153     if( !( p_sys = malloc ( sizeof( *p_sys ) ) ) )
154         return VLC_ENOMEM;
155     p_access->p_sys = p_sys;
156
157     i_dst_port = DEFAULT_PORT;
158     char *psz_parser = psz_dst_addr = strdup( p_access->psz_path );
159     if( !psz_dst_addr )
160     {
161         free( p_sys );
162         return VLC_ENOMEM;
163     }
164
165     if (psz_parser[0] == '[')
166         psz_parser = strchr (psz_parser, ']');
167
168     psz_parser = strchr (psz_parser ? psz_parser : psz_dst_addr, ':');
169     if (psz_parser != NULL)
170     {
171         *psz_parser++ = '\0';
172         i_dst_port = atoi (psz_parser);
173     }
174
175     i_handle = net_ConnectDgram( p_this, psz_dst_addr, i_dst_port, -1,
176                                  IPPROTO_UDP );
177     free (psz_dst_addr);
178
179     if( i_handle == -1 )
180     {
181          msg_Err( p_access, "failed to create raw UDP socket" );
182          free (p_sys);
183          return VLC_EGENERIC;
184     }
185     else
186     {
187         char addr[NI_MAXNUMERICHOST];
188         int port;
189
190         if (net_GetSockAddress (i_handle, addr, &port) == 0)
191         {
192             msg_Dbg (p_access, "source: %s port %d", addr, port);
193             var_SetString (p_access, "src-addr", addr);
194             var_SetInteger (p_access, "src-port", port);
195         }
196
197         if (net_GetPeerAddress (i_handle, addr, &port) == 0)
198         {
199             msg_Dbg (p_access, "destination: %s port %d", addr, port);
200             var_SetString (p_access, "dst-addr", addr);
201             var_SetInteger (p_access, "dst-port", port);
202         }
203     }
204     shutdown( i_handle, SHUT_RD );
205
206     p_sys->i_caching = VLC_TICK_FROM_MS(
207                      var_GetInteger( p_access, SOUT_CFG_PREFIX "caching") );
208     p_sys->i_handle = i_handle;
209     p_sys->i_mtu = var_CreateGetInteger( p_this, "mtu" );
210     p_sys->b_mtu_warning = false;
211     p_sys->dead = false;
212     vlc_queue_Init(&p_sys->queue, offsetof (block_t, p_next));
213     p_sys->p_buffer = NULL;
214
215     if( vlc_clone( &p_sys->thread, ThreadWrite, p_access,
216                            VLC_THREAD_PRIORITY_HIGHEST ) )
217     {
218         msg_Err( p_access, "cannot spawn sout access thread" );
219         net_Close (i_handle);
220         free (p_sys);
221         return VLC_EGENERIC;
222     }
223
224     p_access->pf_write = Write;
225     p_access->pf_control = Control;
226
227     return VLC_SUCCESS;
228 }
229
230 /*****************************************************************************
231  * Close: close the target
232  *****************************************************************************/
233 static void Close( vlc_object_t * p_this )
234 {
235     sout_access_out_t     *p_access = (sout_access_out_t*)p_this;
236     sout_access_out_sys_t *p_sys = p_access->p_sys;
237
238     vlc_queue_Kill(&p_sys->queue, &p_sys->dead);
239     vlc_join( p_sys->thread, NULL );
240
241     if( p_sys->p_buffer ) block_Release( p_sys->p_buffer );
242
243     net_Close( p_sys->i_handle );
244     free( p_sys );
245 }
246
247 static int Control( sout_access_out_t *p_access, int i_query, va_list args )
248 {
249     (void)p_access;
250
251     switch( i_query )
252     {
253         case ACCESS_OUT_CONTROLS_PACE:
254             *va_arg( args, bool * ) = false;
255             break;
256
257         default:
258             return VLC_EGENERIC;
259     }
260     return VLC_SUCCESS;
261 }
262
263 /*****************************************************************************
264  * Write: standard write on a file descriptor.
265  *****************************************************************************/
266 static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
267 {
268     sout_access_out_sys_t *p_sys = p_access->p_sys;
269     int i_len = 0;
270
271     while( p_buffer )
272     {
273         block_t *p_next;
274         int i_packets = 0;
275         vlc_tick_t now = vlc_tick_now();
276
277         if( !p_sys->b_mtu_warning && p_buffer->i_buffer > p_sys->i_mtu )
278         {
279             msg_Warn( p_access, "packet size > MTU, you should probably "
280                       "increase the MTU" );
281             p_sys->b_mtu_warning = true;
282         }
283
284         /* Check if there is enough space in the buffer */
285         if( p_sys->p_buffer &&
286             p_sys->p_buffer->i_buffer + p_buffer->i_buffer > p_sys->i_mtu )
287         {
288             if( p_sys->p_buffer->i_dts + p_sys->i_caching < now )
289             {
290                 msg_Dbg( p_access, "late packet for UDP input (%"PRId64 ")",
291                          now - p_sys->p_buffer->i_dts
292                           - p_sys->i_caching );
293             }
294             vlc_queue_Enqueue(&p_sys->queue, p_sys->p_buffer);
295             p_sys->p_buffer = NULL;
296         }
297
298         i_len += p_buffer->i_buffer;
299         while( p_buffer->i_buffer )
300         {
301             size_t i_payload_size = p_sys->i_mtu;
302             size_t i_write = __MIN( p_buffer->i_buffer, i_payload_size );
303
304             i_packets++;
305
306             if( !p_sys->p_buffer )
307             {
308                 p_sys->p_buffer = block_Alloc( p_sys->i_mtu );
309                 if( !p_sys->p_buffer ) break;
310                 p_sys->p_buffer->i_dts = p_buffer->i_dts;
311                 p_sys->p_buffer->i_buffer = 0;
312             }
313
314             memcpy( p_sys->p_buffer->p_buffer + p_sys->p_buffer->i_buffer,
315                     p_buffer->p_buffer, i_write );
316
317             p_sys->p_buffer->i_buffer += i_write;
318             p_buffer->p_buffer += i_write;
319             p_buffer->i_buffer -= i_write;
320             if ( p_buffer->i_flags & BLOCK_FLAG_CLOCK )
321             {
322                 if ( p_sys->p_buffer->i_flags & BLOCK_FLAG_CLOCK )
323                     msg_Warn( p_access, "putting two PCRs at once" );
324                 p_sys->p_buffer->i_flags |= BLOCK_FLAG_CLOCK;
325             }
326
327             if( p_sys->p_buffer->i_buffer == p_sys->i_mtu || i_packets > 1 )
328             {
329                 /* Flush */
330                 if( p_sys->p_buffer->i_dts + p_sys->i_caching < now )
331                 {
332                     msg_Dbg( p_access, "late packet for udp input (%"PRId64 ")",
333                              vlc_tick_now() - p_sys->p_buffer->i_dts
334                               - p_sys->i_caching );
335                 }
336                 vlc_queue_Enqueue(&p_sys->queue, p_sys->p_buffer);
337                 p_sys->p_buffer = NULL;
338             }
339         }
340
341         p_next = p_buffer->p_next;
342         block_Release( p_buffer );
343         p_buffer = p_next;
344     }
345
346     return i_len;
347 }
348
349 /*****************************************************************************
350  * ThreadWrite: Write a packet on the network at the good time.
351  *****************************************************************************/
352 static void* ThreadWrite( void *data )
353 {
354     sout_access_out_t *p_access = data;
355     sout_access_out_sys_t *p_sys = p_access->p_sys;
356     vlc_tick_t i_date_last = -1;
357     const unsigned i_group = var_GetInteger( p_access,
358                                              SOUT_CFG_PREFIX "group" );
359     int i_to_send = i_group;
360     unsigned i_dropped_packets = 0;
361     block_t *p_pk;
362
363     while ((p_pk = vlc_queue_DequeueKillable(&p_sys->queue,
364                                              &p_sys->dead)) != NULL)
365     {
366         vlc_tick_t    i_date;
367
368         i_date = p_sys->i_caching + p_pk->i_dts;
369         if( i_date_last > 0 )
370         {
371             if( i_date - i_date_last > VLC_TICK_FROM_SEC(2) )
372             {
373                 if( !i_dropped_packets )
374                     msg_Dbg( p_access, "mmh, hole (%"PRId64" > 2s) -> drop",
375                              i_date - i_date_last );
376
377                 block_Release( p_pk );
378
379                 i_date_last = i_date;
380                 i_dropped_packets++;
381                 continue;
382             }
383             else if( i_date - i_date_last < VLC_TICK_FROM_MS(-1) )
384             {
385                 if( !i_dropped_packets )
386                     msg_Dbg( p_access, "mmh, packets in the past (%"PRId64")",
387                              i_date_last - i_date );
388             }
389         }
390
391         i_to_send--;
392         if( !i_to_send || (p_pk->i_flags & BLOCK_FLAG_CLOCK) )
393         {
394             vlc_tick_wait( i_date );
395             i_to_send = i_group;
396         }
397         if ( send( p_sys->i_handle, p_pk->p_buffer, p_pk->i_buffer, 0 ) == -1 )
398             msg_Warn( p_access, "send error: %s", vlc_strerror_c(errno) );
399
400         if( i_dropped_packets )
401         {
402             msg_Dbg( p_access, "dropped %i packets", i_dropped_packets );
403             i_dropped_packets = 0;
404         }
405
406         i_date_last = i_date;
407
408 #if 1
409         i_date = vlc_tick_now() - i_date;
410         if ( i_date > VLC_TICK_FROM_MS(20) )
411         {
412             msg_Dbg( p_access, "packet has been sent too late (%"PRId64 ")",
413                      i_date );
414         }
415 #endif
416
417         block_Release( p_pk );
418
419     }
420     return NULL;
421 }