]> git.sesse.net Git - vlc/blob - modules/access_output/udp.c
Remove excess trainling newlines at end of file.
[vlc] / modules / access_output / udp.c
1 /*****************************************************************************
2  * udp.c
3  *****************************************************************************
4  * Copyright (C) 2001-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Eric Petit <titer@videolan.org>
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <vlc/vlc.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <assert.h>
35
36 #include <vlc_sout.h>
37 #include <vlc_block.h>
38
39 #ifdef HAVE_UNISTD_H
40 #   include <unistd.h>
41 #endif
42
43 #ifdef WIN32
44 #   include <winsock2.h>
45 #   include <ws2tcpip.h>
46 #else
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 #define AUTO_MCAST_TEXT N_("Automatic multicast streaming")
74 #define AUTO_MCAST_LONGTEXT N_("Allocates an outbound multicast address " \
75                                "automatically.")
76
77 vlc_module_begin();
78     set_description( _("UDP stream output") );
79     set_shortname( "UDP" );
80     set_category( CAT_SOUT );
81     set_subcategory( SUBCAT_SOUT_ACO );
82     add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
83     add_integer( SOUT_CFG_PREFIX "group", 1, NULL, GROUP_TEXT, GROUP_LONGTEXT,
84                                  VLC_TRUE );
85     add_obsolete_integer( SOUT_CFG_PREFIX "late" );
86     add_obsolete_bool( SOUT_CFG_PREFIX "raw" );
87     add_bool( SOUT_CFG_PREFIX "auto-mcast", VLC_FALSE, NULL, AUTO_MCAST_TEXT,
88               AUTO_MCAST_LONGTEXT, VLC_TRUE );
89
90     set_capability( "sout access", 100 );
91     add_shortcut( "udp" );
92     set_callbacks( Open, Close );
93 vlc_module_end();
94
95 /*****************************************************************************
96  * Exported prototypes
97  *****************************************************************************/
98
99 static const char *const ppsz_sout_options[] = {
100     "auto-mcast",
101     "caching",
102     "group",
103     NULL
104 };
105
106 /* Options handled by the libvlc network core */
107 static const char *const ppsz_core_options[] = {
108     "dscp",
109     "ttl",
110     "miface",
111     "miface-addr",
112     NULL
113 };
114
115 static int  Write   ( sout_access_out_t *, block_t * );
116 static int  Seek    ( sout_access_out_t *, off_t  );
117
118 static void ThreadWrite( vlc_object_t * );
119 static block_t *NewUDPPacket( sout_access_out_t *, mtime_t );
120 static const char *MakeRandMulticast (int family, char *buf, size_t buflen);
121
122 typedef struct sout_access_thread_t
123 {
124     VLC_COMMON_MEMBERS
125
126     sout_instance_t *p_sout;
127
128     block_fifo_t *p_fifo;
129
130     int         i_handle;
131
132     int64_t     i_caching;
133     int         i_group;
134
135     block_fifo_t *p_empty_blocks;
136 } sout_access_thread_t;
137
138 struct sout_access_out_sys_t
139 {
140     int                 i_mtu;
141     vlc_bool_t          b_mtu_warning;
142
143     block_t             *p_buffer;
144
145     sout_access_thread_t *p_thread;
146
147 };
148
149 #define DEFAULT_PORT 1234
150 #define RTP_HEADER_LENGTH 12
151
152 /*****************************************************************************
153  * Open: open the file
154  *****************************************************************************/
155 static int Open( vlc_object_t *p_this )
156 {
157     sout_access_out_t       *p_access = (sout_access_out_t*)p_this;
158     sout_access_out_sys_t   *p_sys;
159
160     char                *psz_dst_addr = NULL;
161     int                 i_dst_port;
162
163     int                 i_handle;
164
165     config_ChainParse( p_access, SOUT_CFG_PREFIX,
166                        ppsz_sout_options, p_access->p_cfg );
167     config_ChainParse( p_access, "",
168                        ppsz_core_options, p_access->p_cfg );
169
170     if (var_Create (p_access, "dst-port", VLC_VAR_INTEGER)
171      || var_Create (p_access, "src-port", VLC_VAR_INTEGER)
172      || var_Create (p_access, "dst-addr", VLC_VAR_STRING)
173      || var_Create (p_access, "src-addr", VLC_VAR_STRING))
174     {
175         return VLC_ENOMEM;
176     }
177
178     if( !( p_sys = calloc ( 1, sizeof( sout_access_out_sys_t ) ) ) )
179     {
180         msg_Err( p_access, "not enough memory" );
181         return VLC_ENOMEM;
182     }
183     p_access->p_sys = p_sys;
184
185     i_dst_port = DEFAULT_PORT;
186     if (var_GetBool (p_access, SOUT_CFG_PREFIX"auto-mcast"))
187     {
188         char buf[INET6_ADDRSTRLEN];
189         if (MakeRandMulticast (AF_INET, buf, sizeof (buf)) != NULL)
190             psz_dst_addr = strdup (buf);
191     }
192     else
193     {
194         char *psz_parser = psz_dst_addr = strdup( p_access->psz_path );
195
196         if (psz_parser[0] == '[')
197             psz_parser = strchr (psz_parser, ']');
198
199         psz_parser = strchr (psz_parser ?: psz_dst_addr, ':');
200         if (psz_parser != NULL)
201         {
202             *psz_parser++ = '\0';
203             i_dst_port = atoi (psz_parser);
204         }
205     }
206
207     p_sys->p_thread =
208         vlc_object_create( p_access, sizeof( sout_access_thread_t ) );
209     if( !p_sys->p_thread )
210     {
211         msg_Err( p_access, "out of memory" );
212         free (p_sys);
213         free (psz_dst_addr);
214         return VLC_ENOMEM;
215     }
216
217     vlc_object_attach( p_sys->p_thread, p_access );
218     p_sys->p_thread->p_sout = p_access->p_sout;
219     p_sys->p_thread->b_die  = 0;
220     p_sys->p_thread->b_error= 0;
221     p_sys->p_thread->p_fifo = block_FifoNew( p_access );
222     p_sys->p_thread->p_empty_blocks = block_FifoNew( p_access );
223
224     i_handle = net_ConnectDgram( p_this, psz_dst_addr, i_dst_port, -1,
225                                  IPPROTO_UDP );
226     free (psz_dst_addr);
227
228     if( i_handle == -1 )
229     {
230          msg_Err( p_access, "failed to create raw UDP socket" );
231          vlc_object_destroy (p_sys->p_thread);
232          free (p_sys);
233          return VLC_EGENERIC;
234     }
235     else
236     {
237         char addr[NI_MAXNUMERICHOST];
238         int port;
239
240         if (net_GetSockAddress (i_handle, addr, &port) == 0)
241         {
242             msg_Dbg (p_access, "source: %s port %d", addr, port);
243             var_SetString (p_access, "src-addr", addr);
244             var_SetInteger (p_access, "src-port", port);
245         }
246
247         if (net_GetPeerAddress (i_handle, addr, &port) == 0)
248         {
249             msg_Dbg (p_access, "destination: %s port %d", addr, port);
250             var_SetString (p_access, "dst-addr", addr);
251             var_SetInteger (p_access, "dst-port", port);
252         }
253     }
254     p_sys->p_thread->i_handle = i_handle;
255     shutdown( i_handle, SHUT_RD );
256
257     p_sys->p_thread->i_caching =
258         (int64_t)1000 * var_GetInteger( p_access, SOUT_CFG_PREFIX "caching");
259     p_sys->p_thread->i_group =
260         var_GetInteger( p_access, SOUT_CFG_PREFIX "group" );
261
262     p_sys->i_mtu = var_CreateGetInteger( p_this, "mtu" );
263     p_sys->p_buffer = NULL;
264
265     if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite,
266                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
267     {
268         msg_Err( p_access->p_sout, "cannot spawn sout access thread" );
269         net_Close (i_handle);
270         vlc_object_destroy( p_sys->p_thread );
271         free (p_sys);
272         return VLC_EGENERIC;
273     }
274
275     p_access->pf_write = Write;
276     p_access->pf_seek = Seek;
277
278     /* update p_sout->i_out_pace_nocontrol */
279     p_access->p_sout->i_out_pace_nocontrol++;
280
281     return VLC_SUCCESS;
282 }
283
284 /*****************************************************************************
285  * Close: close the target
286  *****************************************************************************/
287 static void Close( vlc_object_t * p_this )
288 {
289     sout_access_out_t     *p_access = (sout_access_out_t*)p_this;
290     sout_access_out_sys_t *p_sys = p_access->p_sys;
291     int i;
292
293     vlc_object_kill( p_sys->p_thread );
294     block_FifoWake( p_sys->p_thread->p_fifo );
295
296     for( i = 0; i < 10; i++ )
297     {
298         block_t *p_dummy = block_New( p_access, p_sys->i_mtu );
299         p_dummy->i_dts = 0;
300         p_dummy->i_pts = 0;
301         p_dummy->i_length = 0;
302         memset( p_dummy->p_buffer, 0, p_dummy->i_buffer );
303         block_FifoPut( p_sys->p_thread->p_fifo, p_dummy );
304     }
305     vlc_thread_join( p_sys->p_thread );
306
307     block_FifoRelease( p_sys->p_thread->p_fifo );
308     block_FifoRelease( p_sys->p_thread->p_empty_blocks );
309
310     if( p_sys->p_buffer ) block_Release( p_sys->p_buffer );
311
312     net_Close( p_sys->p_thread->i_handle );
313
314     vlc_object_detach( p_sys->p_thread );
315     vlc_object_destroy( p_sys->p_thread );
316     /* update p_sout->i_out_pace_nocontrol */
317     p_access->p_sout->i_out_pace_nocontrol--;
318
319     msg_Dbg( p_access, "UDP access output closed" );
320     free( p_sys );
321 }
322
323 /*****************************************************************************
324  * Write: standard write on a file descriptor.
325  *****************************************************************************/
326 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
327 {
328     sout_access_out_sys_t *p_sys = p_access->p_sys;
329     int i_len = 0;
330
331     while( p_buffer )
332     {
333         block_t *p_next;
334         int i_packets = 0;
335         mtime_t now = mdate();
336
337         if( !p_sys->b_mtu_warning && p_buffer->i_buffer > p_sys->i_mtu )
338         {
339             msg_Warn( p_access, "packet size > MTU, you should probably "
340                       "increase the MTU" );
341             p_sys->b_mtu_warning = VLC_TRUE;
342         }
343
344         /* Check if there is enough space in the buffer */
345         if( p_sys->p_buffer &&
346             p_sys->p_buffer->i_buffer + p_buffer->i_buffer > p_sys->i_mtu )
347         {
348             if( p_sys->p_buffer->i_dts + p_sys->p_thread->i_caching < now )
349             {
350                 msg_Dbg( p_access, "late packet for UDP input (" I64Fd ")",
351                          now - p_sys->p_buffer->i_dts
352                           - p_sys->p_thread->i_caching );
353             }
354             block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer );
355             p_sys->p_buffer = NULL;
356         }
357
358         i_len += p_buffer->i_buffer;
359         while( p_buffer->i_buffer )
360         {
361             int i_payload_size = p_sys->i_mtu;
362
363             int i_write = __MIN( p_buffer->i_buffer, i_payload_size );
364
365             i_packets++;
366
367             if( !p_sys->p_buffer )
368             {
369                 p_sys->p_buffer = NewUDPPacket( p_access, p_buffer->i_dts );
370                 if( !p_sys->p_buffer ) break;
371             }
372
373             memcpy( p_sys->p_buffer->p_buffer + p_sys->p_buffer->i_buffer,
374                     p_buffer->p_buffer, i_write );
375
376             p_sys->p_buffer->i_buffer += i_write;
377             p_buffer->p_buffer += i_write;
378             p_buffer->i_buffer -= i_write;
379             if ( p_buffer->i_flags & BLOCK_FLAG_CLOCK )
380             {
381                 if ( p_sys->p_buffer->i_flags & BLOCK_FLAG_CLOCK )
382                     msg_Warn( p_access, "putting two PCRs at once" );
383                 p_sys->p_buffer->i_flags |= BLOCK_FLAG_CLOCK;
384             }
385
386             if( p_sys->p_buffer->i_buffer == p_sys->i_mtu || i_packets > 1 )
387             {
388                 /* Flush */
389                 if( p_sys->p_buffer->i_dts + p_sys->p_thread->i_caching < now )
390                 {
391                     msg_Dbg( p_access, "late packet for udp input (" I64Fd ")",
392                              mdate() - p_sys->p_buffer->i_dts
393                               - p_sys->p_thread->i_caching );
394                 }
395                 block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer );
396                 p_sys->p_buffer = NULL;
397             }
398         }
399
400         p_next = p_buffer->p_next;
401         block_Release( p_buffer );
402         p_buffer = p_next;
403     }
404
405     return( p_sys->p_thread->b_error ? -1 : i_len );
406 }
407
408 /*****************************************************************************
409  * Seek: seek to a specific location in a file
410  *****************************************************************************/
411 static int Seek( sout_access_out_t *p_access, off_t i_pos )
412 {
413     msg_Err( p_access, "UDP sout access cannot seek" );
414     return -1;
415 }
416
417 /*****************************************************************************
418  * NewUDPPacket: allocate a new UDP packet of size p_sys->i_mtu
419  *****************************************************************************/
420 static block_t *NewUDPPacket( sout_access_out_t *p_access, mtime_t i_dts)
421 {
422     sout_access_out_sys_t *p_sys = p_access->p_sys;
423     block_t *p_buffer;
424
425     while ( block_FifoCount( p_sys->p_thread->p_empty_blocks ) > MAX_EMPTY_BLOCKS )
426     {
427         p_buffer = block_FifoGet( p_sys->p_thread->p_empty_blocks );
428         block_Release( p_buffer );
429     }
430
431     if( block_FifoCount( p_sys->p_thread->p_empty_blocks ) == 0 )
432     {
433         p_buffer = block_New( p_access->p_sout, p_sys->i_mtu );
434     }
435     else
436     {
437         p_buffer = block_FifoGet(p_sys->p_thread->p_empty_blocks );
438         p_buffer->i_flags = 0;
439         p_buffer = block_Realloc( p_buffer, 0, p_sys->i_mtu );
440     }
441
442     p_buffer->i_dts = i_dts;
443     p_buffer->i_buffer = 0;
444
445     return p_buffer;
446 }
447
448 /*****************************************************************************
449  * ThreadWrite: Write a packet on the network at the good time.
450  *****************************************************************************/
451 static void ThreadWrite( vlc_object_t *p_this )
452 {
453     sout_access_thread_t *p_thread = (sout_access_thread_t*)p_this;
454     mtime_t              i_date_last = -1;
455     mtime_t              i_to_send = p_thread->i_group;
456     int                  i_dropped_packets = 0;
457
458     while( !p_thread->b_die )
459     {
460         block_t *p_pk;
461         mtime_t       i_date, i_sent;
462 #if 0
463         if( (i++ % 1000)==0 ) {
464           int i = 0;
465           int j = 0;
466           block_t *p_tmp = p_thread->p_empty_blocks->p_first;
467           while( p_tmp ) { p_tmp = p_tmp->p_next; i++;}
468           p_tmp = p_thread->p_fifo->p_first;
469           while( p_tmp ) { p_tmp = p_tmp->p_next; j++;}
470           msg_Dbg( p_thread, "fifo depth: %d/%d, empty blocks: %d/%d",
471                    p_thread->p_fifo->i_depth, j,p_thread->p_empty_blocks->i_depth,i );
472         }
473 #endif
474         p_pk = block_FifoGet( p_thread->p_fifo );
475         if( p_pk == NULL )
476             continue; /* forced wake-up */
477
478         i_date = p_thread->i_caching + p_pk->i_dts;
479         if( i_date_last > 0 )
480         {
481             if( i_date - i_date_last > 2000000 )
482             {
483                 if( !i_dropped_packets )
484                     msg_Dbg( p_thread, "mmh, hole ("I64Fd" > 2s) -> drop",
485                              i_date - i_date_last );
486
487                 block_FifoPut( p_thread->p_empty_blocks, p_pk );
488
489                 i_date_last = i_date;
490                 i_dropped_packets++;
491                 continue;
492             }
493             else if( i_date - i_date_last < -1000 )
494             {
495                 if( !i_dropped_packets )
496                     msg_Dbg( p_thread, "mmh, packets in the past ("I64Fd")",
497                              i_date_last - i_date );
498             }
499         }
500
501         i_to_send--;
502         if( !i_to_send || (p_pk->i_flags & BLOCK_FLAG_CLOCK) )
503         {
504             mwait( i_date );
505             i_to_send = p_thread->i_group;
506         }
507         ssize_t val = send( p_thread->i_handle, p_pk->p_buffer,
508                             p_pk->i_buffer, 0 );
509         if (val == -1)
510         {
511             msg_Warn( p_thread, "send error: %m" );
512         }
513
514         if( i_dropped_packets )
515         {
516             msg_Dbg( p_thread, "dropped %i packets", i_dropped_packets );
517             i_dropped_packets = 0;
518         }
519
520 #if 1
521         i_sent = mdate();
522         if ( i_sent > i_date + 20000 )
523         {
524             msg_Dbg( p_thread, "packet has been sent too late (" I64Fd ")",
525                      i_sent - i_date );
526         }
527 #endif
528
529         block_FifoPut( p_thread->p_empty_blocks, p_pk );
530
531         i_date_last = i_date;
532     }
533 }
534
535
536 static const char *MakeRandMulticast (int family, char *buf, size_t buflen)
537 {
538     uint32_t rand = (getpid() & 0xffff)
539                   | (uint32_t)(((mdate () >> 10) & 0xffff) << 16);
540
541     switch (family)
542     {
543 #ifdef AF_INET6
544         case AF_INET6:
545         {
546             struct in6_addr addr;
547             memcpy (&addr, "\xff\x38\x00\x00" "\x00\x00\x00\x00"
548                            "\x00\x00\x00\x00", 12);
549             rand |= 0x80000000;
550             memcpy (addr.s6_addr + 12, &(uint32_t){ htonl (rand) }, 4);
551             return inet_ntop (family, &addr, buf, buflen);
552         }
553 #endif
554
555         case AF_INET:
556         {
557             struct in_addr addr;
558             addr.s_addr = htonl ((rand & 0xffffff) | 0xe8000000);
559             return inet_ntop (family, &addr, buf, buflen);
560         }
561     }
562 #ifdef EAFNOSUPPORT
563     errno = EAFNOSUPPORT;
564 #endif
565     return NULL;
566 }