]> git.sesse.net Git - vlc/blob - modules/access_output/udp.c
* access_output: sout_buffer_t -> block_t.
[vlc] / modules / access_output / udp.c
1 /*****************************************************************************
2  * udp.c
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <fcntl.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/sout.h>
37
38 #ifdef HAVE_UNISTD_H
39 #   include <unistd.h>
40 #endif
41
42 #ifdef WIN32
43 #   include <winsock2.h>
44 #   include <ws2tcpip.h>
45 #   ifndef IN_MULTICAST
46 #       define IN_MULTICAST(a) IN_CLASSD(a)
47 #   endif
48 #else
49 #   include <sys/socket.h>
50 #endif
51
52 #include "network.h"
53
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58 static int  Open ( vlc_object_t * );
59 static void Close( vlc_object_t * );
60
61 #define CACHING_TEXT N_("Caching value (ms)")
62 #define CACHING_LONGTEXT N_( \
63     "Allows you to modify the default caching value for udp streams. This " \
64     "value should be set in millisecond units." )
65
66 vlc_module_begin();
67     set_description( _("UDP stream ouput") );
68     add_integer( "udp-sout-caching", DEFAULT_PTS_DELAY / 1000, NULL,
69                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
70     set_capability( "sout access", 100 );
71     add_shortcut( "udp" );
72     add_shortcut( "rtp" ); // Will work only with ts muxer
73     set_callbacks( Open, Close );
74 vlc_module_end();
75
76 /*****************************************************************************
77  * Exported prototypes
78  *****************************************************************************/
79 static int  Write   ( sout_access_out_t *, block_t * );
80 static int  WriteRaw( sout_access_out_t *, block_t * );
81 static int  Seek    ( sout_access_out_t *, off_t  );
82
83 static void ThreadWrite( vlc_object_t * );
84
85 static block_t *NewUDPPacket( sout_access_out_t *, mtime_t );
86
87
88 typedef struct sout_access_thread_t
89 {
90     VLC_COMMON_MEMBERS
91
92     sout_instance_t *p_sout;
93
94     block_fifo_t *p_fifo;
95
96     int         i_handle;
97
98     int64_t     i_caching;
99     int64_t     i_late;
100     int         i_group;
101
102 } sout_access_thread_t;
103
104 struct sout_access_out_sys_t
105 {
106     int                 b_rtpts;  // 1 if add rtp/ts header
107     uint16_t            i_sequence_number;
108     uint32_t            i_ssrc;
109
110     unsigned int        i_mtu;
111
112     block_t             *p_buffer;
113
114     sout_access_thread_t *p_thread;
115
116 };
117
118 #define DEFAULT_PORT 1234
119
120 /*****************************************************************************
121  * Open: open the file
122  *****************************************************************************/
123 static int Open( vlc_object_t *p_this )
124 {
125     sout_access_out_t       *p_access = (sout_access_out_t*)p_this;
126     sout_access_out_sys_t   *p_sys;
127
128     char                *psz_parser;
129     char                *psz_dst_addr;
130     int                 i_dst_port;
131
132     module_t            *p_network;
133     network_socket_t    socket_desc;
134
135     vlc_value_t         val;
136     char                *psz_val;
137
138     if( !( p_sys = p_access->p_sys =
139                 malloc( sizeof( sout_access_out_sys_t ) ) ) )
140     {
141         msg_Err( p_access, "not enough memory" );
142         return VLC_EGENERIC;
143     }
144
145
146     if( p_access->psz_access != NULL &&
147         !strcmp( p_access->psz_access, "rtp" ) )
148     {
149         msg_Warn( p_access, "be careful that rtp output only works with ts "
150                   "payload (not an error)" );
151         p_sys->b_rtpts = 1;
152     }
153     else
154     {
155         p_sys->b_rtpts = 0;
156     }
157
158     psz_parser = strdup( p_access->psz_name );
159
160     psz_dst_addr = psz_parser;
161     i_dst_port = 0;
162
163     if ( *psz_parser == '[' )
164     {
165         while( *psz_parser && *psz_parser != ']' )
166         {
167             psz_parser++;
168         }
169     }
170     while( *psz_parser && *psz_parser != ':' )
171     {
172         psz_parser++;
173     }
174     if( *psz_parser == ':' )
175     {
176         *psz_parser = '\0';
177         psz_parser++;
178         i_dst_port = atoi( psz_parser );
179     }
180     if( i_dst_port <= 0 )
181     {
182         i_dst_port = DEFAULT_PORT;
183     }
184
185     p_sys->p_thread =
186         vlc_object_create( p_access, sizeof( sout_access_thread_t ) );
187     if( !p_sys->p_thread )
188     {
189         msg_Err( p_access, "out of memory" );
190         return VLC_EGENERIC;
191     }
192
193     p_sys->p_thread->p_sout = p_access->p_sout;
194     p_sys->p_thread->b_die  = 0;
195     p_sys->p_thread->b_error= 0;
196     p_sys->p_thread->p_fifo = block_FifoNew( p_access );
197
198     socket_desc.i_type = NETWORK_UDP;
199     socket_desc.psz_server_addr = psz_dst_addr;
200     socket_desc.i_server_port   = i_dst_port;
201     socket_desc.psz_bind_addr   = "";
202     socket_desc.i_bind_port     = 0;
203     socket_desc.i_ttl           = 0;
204     if( ( psz_val = sout_cfg_find_value( p_access->p_cfg, "ttl" ) ) )
205     {
206         socket_desc.i_ttl = atoi( psz_val );
207     }
208     p_sys->p_thread->p_private = (void*)&socket_desc;
209     if( !( p_network = module_Need( p_sys->p_thread, "network", NULL, 0 ) ) )
210     {
211         msg_Err( p_access, "failed to open a connection (udp)" );
212         return VLC_EGENERIC;
213     }
214     module_Unneed( p_sys->p_thread, p_network );
215
216     p_sys->p_thread->i_handle = socket_desc.i_handle;
217
218     var_Create( p_this, "udp-sout-caching",
219                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
220     var_Get( p_this, "udp-sout-caching", &val );
221     p_sys->p_thread->i_caching = val.i_int * 1000;
222     if( ( psz_val = sout_cfg_find_value( p_access->p_cfg, "caching" ) ) )
223     {
224         p_sys->p_thread->i_caching = atoll( psz_val ) * 1000;
225     }
226
227     p_sys->p_thread->i_group = 1;
228     if( ( psz_val = sout_cfg_find_value( p_access->p_cfg, "group" ) ) )
229     {
230         p_sys->p_thread->i_group = atoi( psz_val );
231     }
232
233     p_sys->p_thread->i_late = 0;
234     if( ( psz_val = sout_cfg_find_value( p_access->p_cfg, "late" ) ) )
235     {
236         p_sys->p_thread->i_late = atoll( psz_val ) * 1000;
237     }
238
239
240     p_sys->i_mtu = socket_desc.i_mtu;
241
242 #ifdef WIN32
243     if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite,
244                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
245 #else
246     if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite,
247                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
248 #endif
249     {
250         msg_Err( p_access->p_sout, "cannot spawn sout access thread" );
251         vlc_object_destroy( p_sys->p_thread );
252         return VLC_EGENERIC;
253     }
254
255     srand( (uint32_t)mdate());
256     p_sys->p_buffer          = NULL;
257     p_sys->i_sequence_number = rand()&0xffff;
258     p_sys->i_ssrc            = rand()&0xffffffff;
259
260     if( sout_cfg_find( p_access->p_cfg, "raw" ) )
261     {
262         p_access->pf_write = WriteRaw;
263     }
264     else
265     {
266         p_access->pf_write = Write;
267     }
268
269     p_access->pf_seek = Seek;
270
271     msg_Dbg( p_access, "udp access output opened(%s:%d)", psz_dst_addr, i_dst_port );
272
273     free( psz_dst_addr );
274
275     /* update p_sout->i_out_pace_nocontrol */
276     p_access->p_sout->i_out_pace_nocontrol++;
277
278     return VLC_SUCCESS;
279 }
280
281 /*****************************************************************************
282  * Close: close the target
283  *****************************************************************************/
284 static void Close( vlc_object_t * p_this )
285 {
286     sout_access_out_t     *p_access = (sout_access_out_t*)p_this;
287     sout_access_out_sys_t *p_sys = p_access->p_sys;
288     int i;
289
290     p_sys->p_thread->b_die = 1;
291     for( i = 0; i < 10; i++ )
292     {
293         block_t *p_dummy = block_New( p_access, p_sys->i_mtu );
294         p_dummy->i_dts = 0;
295         p_dummy->i_pts = 0;
296         p_dummy->i_length = 0;
297         block_FifoPut( p_sys->p_thread->p_fifo, p_dummy );
298     }
299     vlc_thread_join( p_sys->p_thread );
300
301     block_FifoRelease( p_sys->p_thread->p_fifo );
302
303     if( p_sys->p_buffer )
304     {
305         block_Release( p_sys->p_buffer );
306     }
307
308     net_Close( p_sys->p_thread->i_handle );
309
310     /* update p_sout->i_out_pace_nocontrol */
311     p_access->p_sout->i_out_pace_nocontrol--;
312
313     msg_Dbg( p_access, "udp access output closed" );
314     free( p_sys );
315 }
316
317 /*****************************************************************************
318  * Write: standard write on a file descriptor.
319  *****************************************************************************/
320 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
321 {
322     sout_access_out_sys_t *p_sys = p_access->p_sys;
323     unsigned int i_write;
324
325     while( p_buffer )
326     {
327         block_t *p_next;
328         if( p_buffer->i_buffer > p_sys->i_mtu )
329         {
330             msg_Warn( p_access, "arggggggggggggg packet size > mtu" );
331             i_write = p_sys->i_mtu;
332         }
333         else
334         {
335             i_write = p_buffer->i_buffer;
336         }
337
338         /* if we have enough data, enque the buffer */
339         if( p_sys->p_buffer &&
340             p_sys->p_buffer->i_buffer + i_write > p_sys->i_mtu )
341         {
342             block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer );
343             p_sys->p_buffer = NULL;
344         }
345
346         if( !p_sys->p_buffer )
347         {
348             p_sys->p_buffer = NewUDPPacket( p_access, p_buffer->i_dts );
349         }
350
351         if( p_buffer->i_buffer > 0 )
352         {
353             memcpy( p_sys->p_buffer->p_buffer + p_sys->p_buffer->i_buffer,
354                     p_buffer->p_buffer, i_write );
355             p_sys->p_buffer->i_buffer += i_write;
356         }
357         p_next = p_buffer->p_next;
358         block_Release( p_buffer );
359         p_buffer = p_next;
360     }
361
362     return( p_sys->p_thread->b_error ? -1 : 0 );
363 }
364
365 /*****************************************************************************
366  * WriteRaw: write p_buffer without trying to fill mtu
367  *****************************************************************************/
368 static int WriteRaw( sout_access_out_t *p_access, block_t *p_buffer )
369 {
370     sout_access_out_sys_t   *p_sys = p_access->p_sys;
371
372     block_FifoPut( p_sys->p_thread->p_fifo, p_buffer );
373
374     return( p_sys->p_thread->b_error ? -1 : 0 );
375 }
376
377 /*****************************************************************************
378  * Seek: seek to a specific location in a file
379  *****************************************************************************/
380 static int Seek( sout_access_out_t *p_access, off_t i_pos )
381 {
382     msg_Err( p_access, "UDP sout access cannot seek" );
383     return -1;
384 }
385
386 /*****************************************************************************
387  * NewUDPPacket: allocate a new UDP packet of size p_sys->i_mtu
388  *****************************************************************************/
389 static block_t *NewUDPPacket( sout_access_out_t *p_access, mtime_t i_dts)
390 {
391     sout_access_out_sys_t *p_sys = p_access->p_sys;
392     block_t *p_buffer;
393
394     p_buffer = block_New( p_access->p_sout, p_sys->i_mtu );
395     p_buffer->i_dts = i_dts;
396     p_buffer->i_buffer = 0;
397
398     if( p_sys->b_rtpts )
399     {
400         mtime_t i_timestamp = p_buffer->i_dts * 9 / 100;
401
402         /* add rtp/ts header */
403         p_buffer->p_buffer[0] = 0x80;
404         p_buffer->p_buffer[1] = 0x21; // mpeg2-ts
405
406         p_buffer->p_buffer[2] = ( p_sys->i_sequence_number >> 8 )&0xff;
407         p_buffer->p_buffer[3] = p_sys->i_sequence_number&0xff;
408         p_sys->i_sequence_number++;
409
410         p_buffer->p_buffer[4] = ( i_timestamp >> 24 )&0xff;
411         p_buffer->p_buffer[5] = ( i_timestamp >> 16 )&0xff;
412         p_buffer->p_buffer[6] = ( i_timestamp >>  8 )&0xff;
413         p_buffer->p_buffer[7] = i_timestamp&0xff;
414
415         p_buffer->p_buffer[ 8] = ( p_sys->i_ssrc >> 24 )&0xff;
416         p_buffer->p_buffer[ 9] = ( p_sys->i_ssrc >> 16 )&0xff;
417         p_buffer->p_buffer[10] = ( p_sys->i_ssrc >>  8 )&0xff;
418         p_buffer->p_buffer[11] = p_sys->i_ssrc&0xff;
419
420         p_buffer->i_buffer = 12;
421     }
422
423     return p_buffer;
424 }
425
426 /*****************************************************************************
427  * ThreadWrite: Write a packet on the network at the good time.
428  *****************************************************************************/
429 static void ThreadWrite( vlc_object_t *p_this )
430 {
431     sout_access_thread_t *p_thread = (sout_access_thread_t*)p_this;
432     mtime_t              i_date_last = -1;
433     mtime_t              i_to_send = p_thread->i_group;
434     int                  i_dropped_packets = 0;
435
436     while( !p_thread->b_die )
437     {
438         block_t *p_pk;
439         mtime_t       i_date, i_sent;
440
441         p_pk = block_FifoGet( p_thread->p_fifo );
442
443         i_date = p_thread->i_caching + p_pk->i_dts;
444         if( i_date_last > 0 )
445         {
446             if( i_date - i_date_last > 2000000 )
447             {
448                 if( !i_dropped_packets )
449                     msg_Dbg( p_thread, "mmh, hole > 2s -> drop" );
450
451                 block_Release( p_pk  );
452                 i_date_last = i_date;
453                 i_dropped_packets++;
454                 continue;
455             }
456             else if( i_date - i_date_last < 0 )
457             {
458                 if( !i_dropped_packets )
459                     msg_Dbg( p_thread, "mmh, paquets in the past -> drop" );
460
461                 block_Release( p_pk  );
462                 i_date_last = i_date;
463                 i_dropped_packets++;
464                 continue;
465             }
466         }
467
468         i_sent = mdate();
469         if( p_thread->i_late > 0 && i_sent > i_date + p_thread->i_late )
470         {
471             if( !i_dropped_packets )
472             {
473                 msg_Dbg( p_thread, "late packet to send (" I64Fd ") -> drop",
474                          i_sent - i_date );
475             }
476             block_Release( p_pk  );
477             i_date_last = i_date;
478             i_dropped_packets++;
479             continue;
480         }
481
482         i_to_send--;
483         if ( !i_to_send )
484         {
485             mwait( i_date );
486             i_to_send = p_thread->i_group;
487         }
488         send( p_thread->i_handle, p_pk->p_buffer, p_pk->i_buffer, 0 );
489
490         if( i_dropped_packets )
491         {
492             msg_Dbg( p_thread, "dropped %i packets", i_dropped_packets );
493             i_dropped_packets = 0;
494         }
495
496 #if 0
497         i_sent = mdate();
498         if ( i_sent > i_date + 20000 )
499         {
500             msg_Dbg( p_thread, "packet has been sent too late (" I64Fd ")",
501                      i_sent - i_date );
502         }
503 #endif
504
505         block_Release( p_pk  );
506         i_date_last = i_date;
507     }
508 }