udp.c 19.7 KB
Newer Older
1
2
3
/*****************************************************************************
 * udp.c
 *****************************************************************************
4
 * Copyright (C) 2001-2005 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Eric Petit <titer@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#include <vlc/vlc.h>
#include <vlc/sout.h>

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif

#ifdef WIN32
#   include <winsock2.h>
#   include <ws2tcpip.h>
#   ifndef IN_MULTICAST
#       define IN_MULTICAST(a) IN_CLASSD(a)
#   endif
#else
#   include <sys/socket.h>
#endif

#include "network.h"

54
#define MAX_EMPTY_BLOCKS 200
55

56
57
58
59
60
61
62
63
64
65
66
#if defined(WIN32) || defined(UNDER_CE)
# define WINSOCK_STRERROR_SIZE 20
static const char *winsock_strerror( char *buf )
{
    snprintf( buf, WINSOCK_STRERROR_SIZE, "Winsock error %d",
              WSAGetLastError( ) );
    buf[WINSOCK_STRERROR_SIZE - 1] = '\0';
    return buf;
}
#endif

67
68
69
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
70
71
72
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

Laurent Aimar's avatar
Laurent Aimar committed
73
74
#define SOUT_CFG_PREFIX "sout-udp-"

75
#define CACHING_TEXT N_("Caching value (ms)")
gbazin's avatar
   
gbazin committed
76
#define CACHING_LONGTEXT N_( \
77
    "Allows you to modify the default caching value for UDP streams. This " \
gbazin's avatar
   
gbazin committed
78
    "value should be set in millisecond units." )
gbazin's avatar
   
gbazin committed
79

zorglub's avatar
zorglub committed
80
81
82
83
84
85
86
#define TTL_TEXT N_("Time To Live")
#define TTL_LONGTEXT N_("Allows you to define the time to live of the " \
                        "outgoing stream.")

#define GROUP_TEXT N_("Group packets")
#define GROUP_LONGTEXT N_("Packets can be sent one by one at the right time " \
                          "or by groups. This allows you to give the number " \
87
88
89
                          "of packets that will be sent at a time. It " \
                          "helps reducing the scheduling load on " \
                          "heavily-loaded systems." )
zorglub's avatar
zorglub committed
90
91
92
93
94
95
#define RAW_TEXT N_("Raw write")
#define RAW_LONGTEXT N_("If you enable this option, packets will be sent " \
                       "directly, without trying to fill the MTU (ie, " \
                       "without trying to make the biggest possible packets " \
                       "in order to improve streaming)." )

96
vlc_module_begin();
97
    set_description( _("UDP stream output") );
zorglub's avatar
   
zorglub committed
98
    set_shortname( N_( "UDP" ) );
zorglub's avatar
zorglub committed
99
100
    set_category( CAT_SOUT );
    set_subcategory( SUBCAT_SOUT_ACO );
Laurent Aimar's avatar
Laurent Aimar committed
101
    add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
zorglub's avatar
zorglub committed
102
103
104
105
    add_integer( SOUT_CFG_PREFIX "ttl", 0, NULL,TTL_TEXT, TTL_LONGTEXT,
                                 VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "group", 1, NULL, GROUP_TEXT, GROUP_LONGTEXT,
                                 VLC_TRUE );
106
    add_suppressed_integer( SOUT_CFG_PREFIX "late" );
zorglub's avatar
zorglub committed
107
108
    add_bool( SOUT_CFG_PREFIX "raw",  0, NULL, RAW_TEXT, RAW_LONGTEXT,
                                 VLC_TRUE );
Laurent Aimar's avatar
Laurent Aimar committed
109

110
111
112
113
114
115
    set_capability( "sout access", 100 );
    add_shortcut( "udp" );
    add_shortcut( "rtp" ); // Will work only with ts muxer
    set_callbacks( Open, Close );
vlc_module_end();

116
117
118
/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
119
120
121
122
123
124
125
126
127

static const char *ppsz_sout_options[] = {
    "caching",
    "ttl",
    "group",
    "raw",
    NULL
};

128
129
130
131
132
133
134
static int  Write   ( sout_access_out_t *, block_t * );
static int  WriteRaw( sout_access_out_t *, block_t * );
static int  Seek    ( sout_access_out_t *, off_t  );

static void ThreadWrite( vlc_object_t * );
static block_t *NewUDPPacket( sout_access_out_t *, mtime_t );

135
typedef struct sout_access_thread_t
136
137
138
139
140
{
    VLC_COMMON_MEMBERS

    sout_instance_t *p_sout;

141
    block_fifo_t *p_fifo;
142
143
144

    int         i_handle;

145
    int64_t     i_caching;
146
    int         i_group;
147

148
149
150
151
    vlc_mutex_t blocks_lock;
    block_t     *p_empty_blocks;
    int         i_empty_depth;

152
153
} sout_access_thread_t;

Laurent Aimar's avatar
Laurent Aimar committed
154
struct sout_access_out_sys_t
155
156
157
158
159
{
    int                 b_rtpts;  // 1 if add rtp/ts header
    uint16_t            i_sequence_number;
    uint32_t            i_ssrc;

160
    int                 i_mtu;
161

162
    block_t             *p_buffer;
163
164
165

    sout_access_thread_t *p_thread;

166
    vlc_bool_t          b_mtu_warning;
Laurent Aimar's avatar
Laurent Aimar committed
167
};
168

169
170
#define DEFAULT_PORT 1234

171
172
173
174
175
/*****************************************************************************
 * Open: open the file
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
Laurent Aimar's avatar
Laurent Aimar committed
176
177
    sout_access_out_t       *p_access = (sout_access_out_t*)p_this;
    sout_access_out_sys_t   *p_sys;
178
179
180
181
182
183
184
185

    char                *psz_parser;
    char                *psz_dst_addr;
    int                 i_dst_port;

    module_t            *p_network;
    network_socket_t    socket_desc;

gbazin's avatar
   
gbazin committed
186
    vlc_value_t         val;
Laurent Aimar's avatar
Laurent Aimar committed
187

188
189
    sout_CfgParse( p_access, SOUT_CFG_PREFIX,
                   ppsz_sout_options, p_access->p_cfg );
190

191
    if( !( p_sys = malloc( sizeof( sout_access_out_sys_t ) ) ) )
192
    {
193
        msg_Err( p_access, "not enough memory" );
gbazin's avatar
   
gbazin committed
194
        return VLC_EGENERIC;
195
    }
196
197
    memset( p_sys, 0, sizeof(sout_access_out_sys_t) );
    p_access->p_sys = p_sys;
198

Laurent Aimar's avatar
Laurent Aimar committed
199
200
    if( p_access->psz_access != NULL &&
        !strcmp( p_access->psz_access, "rtp" ) )
201
    {
202
203
        msg_Warn( p_access, "be careful that rtp output only works with ts "
                  "payload (not an error)" );
Laurent Aimar's avatar
Laurent Aimar committed
204
        p_sys->b_rtpts = 1;
205
206
207
    }
    else
    {
Laurent Aimar's avatar
Laurent Aimar committed
208
        p_sys->b_rtpts = 0;
209
210
    }

Laurent Aimar's avatar
Laurent Aimar committed
211
    psz_parser = strdup( p_access->psz_name );
212
213
214
215

    psz_dst_addr = psz_parser;
    i_dst_port = 0;

216
217
218
219
220
221
222
    if ( *psz_parser == '[' )
    {
        while( *psz_parser && *psz_parser != ']' )
        {
            psz_parser++;
        }
    }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    while( *psz_parser && *psz_parser != ':' )
    {
        psz_parser++;
    }
    if( *psz_parser == ':' )
    {
        *psz_parser = '\0';
        psz_parser++;
        i_dst_port = atoi( psz_parser );
    }
    if( i_dst_port <= 0 )
    {
        i_dst_port = DEFAULT_PORT;
    }

Laurent Aimar's avatar
Laurent Aimar committed
238
    p_sys->p_thread =
gbazin's avatar
   
gbazin committed
239
        vlc_object_create( p_access, sizeof( sout_access_thread_t ) );
Laurent Aimar's avatar
Laurent Aimar committed
240
    if( !p_sys->p_thread )
241
    {
Laurent Aimar's avatar
Laurent Aimar committed
242
        msg_Err( p_access, "out of memory" );
gbazin's avatar
   
gbazin committed
243
        return VLC_EGENERIC;
244
245
    }

Laurent Aimar's avatar
Laurent Aimar committed
246
247
248
    p_sys->p_thread->p_sout = p_access->p_sout;
    p_sys->p_thread->b_die  = 0;
    p_sys->p_thread->b_error= 0;
249
    p_sys->p_thread->p_fifo = block_FifoNew( p_access );
250
251
252
    p_sys->p_thread->p_empty_blocks = NULL;
    p_sys->p_thread->i_empty_depth = 0;
    vlc_mutex_init( p_access, &p_sys->p_thread->blocks_lock );
253

254
    /* FIXME: use net_OpenUDP API */
255
256
257
258
    socket_desc.psz_server_addr = psz_dst_addr;
    socket_desc.i_server_port   = i_dst_port;
    socket_desc.psz_bind_addr   = "";
    socket_desc.i_bind_port     = 0;
259
260
    socket_desc.i_handle        = -1;
    socket_desc.v6only          = 0;
Laurent Aimar's avatar
Laurent Aimar committed
261
262
263
264

    var_Get( p_access, SOUT_CFG_PREFIX "ttl", &val );
    socket_desc.i_ttl = val.i_int;

Laurent Aimar's avatar
Laurent Aimar committed
265
    p_sys->p_thread->p_private = (void*)&socket_desc;
266
267
268
269
270
    p_network = module_Need( p_sys->p_thread, "network", "ipv4", VLC_TRUE );
    if( p_network != NULL )
        module_Unneed( p_sys->p_thread, p_network );

    if( socket_desc.i_handle == -1 )
271
    {
272
273
274
275
276
277
278
279
280
        p_network = module_Need( p_sys->p_thread, "network", "ipv6", VLC_TRUE );
        if( p_network != NULL )
            module_Unneed( p_sys->p_thread, p_network );

        if( socket_desc.i_handle == -1 )
        {
            msg_Err( p_access, "failed to open a connection (udp)" );
            return VLC_EGENERIC;
        }
281
282
    }

Laurent Aimar's avatar
Laurent Aimar committed
283
    p_sys->p_thread->i_handle = socket_desc.i_handle;
284
    net_StopRecv( socket_desc.i_handle );
gbazin's avatar
   
gbazin committed
285

Laurent Aimar's avatar
Laurent Aimar committed
286
287
    var_Get( p_access, SOUT_CFG_PREFIX "caching", &val );
    p_sys->p_thread->i_caching = (int64_t)val.i_int * 1000;
288

Laurent Aimar's avatar
Laurent Aimar committed
289
290
    var_Get( p_access, SOUT_CFG_PREFIX "group", &val );
    p_sys->p_thread->i_group = val.i_int;
291

gbazin's avatar
   
gbazin committed
292
    p_sys->i_mtu = socket_desc.i_mtu;
293

gbazin's avatar
gbazin committed
294
295
    if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite,
                           VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
296
    {
Laurent Aimar's avatar
Laurent Aimar committed
297
298
        msg_Err( p_access->p_sout, "cannot spawn sout access thread" );
        vlc_object_destroy( p_sys->p_thread );
gbazin's avatar
   
gbazin committed
299
        return VLC_EGENERIC;
300
301
    }

302
    srand( (uint32_t)mdate());
Laurent Aimar's avatar
Laurent Aimar committed
303
    p_sys->p_buffer          = NULL;
304
305
    p_sys->i_sequence_number = rand()&0xffff;
    p_sys->i_ssrc            = rand()&0xffffffff;
306

Laurent Aimar's avatar
Laurent Aimar committed
307
    var_Get( p_access, SOUT_CFG_PREFIX "raw", &val );
308
309
    if( val.b_bool )  p_access->pf_write = WriteRaw;
    else p_access->pf_write = Write;
310

gbazin's avatar
   
gbazin committed
311
312
    p_access->pf_seek = Seek;

313
314
    msg_Dbg( p_access, "udp access output opened(%s:%d)",
             psz_dst_addr, i_dst_port );
315
316

    free( psz_dst_addr );
317
318
319
320

    /* update p_sout->i_out_pace_nocontrol */
    p_access->p_sout->i_out_pace_nocontrol++;

321
322
323
324
325
326
327
328
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close: close the target
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
gbazin's avatar
   
gbazin committed
329
330
331
    sout_access_out_t     *p_access = (sout_access_out_t*)p_this;
    sout_access_out_sys_t *p_sys = p_access->p_sys;
    int i;
332

Laurent Aimar's avatar
Laurent Aimar committed
333
    p_sys->p_thread->b_die = 1;
334
335
    for( i = 0; i < 10; i++ )
    {
336
        block_t *p_dummy = block_New( p_access, p_sys->i_mtu );
337
338
339
        p_dummy->i_dts = 0;
        p_dummy->i_pts = 0;
        p_dummy->i_length = 0;
340
        block_FifoPut( p_sys->p_thread->p_fifo, p_dummy );
341
    }
Laurent Aimar's avatar
Laurent Aimar committed
342
    vlc_thread_join( p_sys->p_thread );
343

344
    block_FifoRelease( p_sys->p_thread->p_fifo );
345

346
    if( p_sys->p_buffer ) block_Release( p_sys->p_buffer );
347
348
349
350
351
352
353
    while ( p_sys->p_thread->p_empty_blocks != NULL )
    {
        block_t *p_next = p_sys->p_thread->p_empty_blocks->p_next;
        block_Release( p_sys->p_thread->p_empty_blocks );
        p_sys->p_thread->p_empty_blocks = p_next;
    }
    vlc_mutex_destroy( &p_sys->p_thread->blocks_lock );
354

355
356
357
358
    net_Close( p_sys->p_thread->i_handle );

    /* update p_sout->i_out_pace_nocontrol */
    p_access->p_sout->i_out_pace_nocontrol--;
Laurent Aimar's avatar
Laurent Aimar committed
359

360
    msg_Dbg( p_access, "udp access output closed" );
Laurent Aimar's avatar
Laurent Aimar committed
361
    free( p_sys );
362
363
364
}

/*****************************************************************************
365
 * Write: standard write on a file descriptor.
366
 *****************************************************************************/
367
static int Write( sout_access_out_t *p_access, block_t *p_buffer )
368
{
gbazin's avatar
   
gbazin committed
369
    sout_access_out_sys_t *p_sys = p_access->p_sys;
370
371
372

    while( p_buffer )
    {
373
        block_t *p_next;
374
375
376
        int i_packets = 0;

        if( !p_sys->b_mtu_warning && p_buffer->i_buffer > p_sys->i_mtu )
377
        {
378
379
380
            msg_Warn( p_access, "packet size > MTU, you should probably "
                      "increase the MTU" );
            p_sys->b_mtu_warning = VLC_TRUE;
381
382
        }

383
        /* Check if there is enough space in the buffer */
Laurent Aimar's avatar
Laurent Aimar committed
384
        if( p_sys->p_buffer &&
385
            p_sys->p_buffer->i_buffer + p_buffer->i_buffer > p_sys->i_mtu )
386
        {
387
388
389
390
391
392
            if( p_sys->p_buffer->i_dts + p_sys->p_thread->i_caching < mdate() )
            {
                msg_Dbg( p_access, "late packet for udp input (" I64Fd ")",
                         mdate() - p_sys->p_buffer->i_dts
                          - p_sys->p_thread->i_caching );
            }
393
            block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer );
Laurent Aimar's avatar
Laurent Aimar committed
394
            p_sys->p_buffer = NULL;
395
396
        }

397
        while( p_buffer->i_buffer )
398
        {
399
400
401
402
403
404
405
406
407
            int i_write = __MIN( p_buffer->i_buffer, p_sys->i_mtu );

            i_packets++;

            if( !p_sys->p_buffer )
            {
                p_sys->p_buffer = NewUDPPacket( p_access, p_buffer->i_dts );
                if( !p_sys->p_buffer ) break;
            }
408

409
            memcpy( p_sys->p_buffer->p_buffer + p_sys->p_buffer->i_buffer,
gbazin's avatar
   
gbazin committed
410
                    p_buffer->p_buffer, i_write );
411

412
            p_sys->p_buffer->i_buffer += i_write;
413
414
            p_buffer->p_buffer += i_write;
            p_buffer->i_buffer -= i_write;
415
            if ( p_buffer->i_flags & BLOCK_FLAG_CLOCK )
416
417
418
            {
                if ( p_sys->p_buffer->i_flags & BLOCK_FLAG_CLOCK )
                    msg_Warn( p_access, "putting two PCRs at once" );
419
                p_sys->p_buffer->i_flags |= BLOCK_FLAG_CLOCK;
420
            }
421
422
423
424

            if( p_sys->p_buffer->i_buffer == p_sys->i_mtu || i_packets > 1 )
            {
                /* Flush */
425
426
427
428
429
430
431
                if( p_sys->p_buffer->i_dts + p_sys->p_thread->i_caching
                      < mdate() )
                {
                    msg_Dbg( p_access, "late packet for udp input (" I64Fd ")",
                             mdate() - p_sys->p_buffer->i_dts
                              - p_sys->p_thread->i_caching );
                }
432
433
434
                block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer );
                p_sys->p_buffer = NULL;
            }
435
        }
436

437
        p_next = p_buffer->p_next;
438
        block_Release( p_buffer );
439
440
441
        p_buffer = p_next;
    }

Laurent Aimar's avatar
Laurent Aimar committed
442
    return( p_sys->p_thread->b_error ? -1 : 0 );
443
444
}

445
446
447
/*****************************************************************************
 * WriteRaw: write p_buffer without trying to fill mtu
 *****************************************************************************/
448
static int WriteRaw( sout_access_out_t *p_access, block_t *p_buffer )
449
450
451
{
    sout_access_out_sys_t   *p_sys = p_access->p_sys;

452
    block_FifoPut( p_sys->p_thread->p_fifo, p_buffer );
453
454
455
456

    return( p_sys->p_thread->b_error ? -1 : 0 );
}

457
458
459
/*****************************************************************************
 * Seek: seek to a specific location in a file
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
460
static int Seek( sout_access_out_t *p_access, off_t i_pos )
461
{
462
    msg_Err( p_access, "UDP sout access cannot seek" );
463
    return -1;
464
465
}

gbazin's avatar
   
gbazin committed
466
467
468
/*****************************************************************************
 * NewUDPPacket: allocate a new UDP packet of size p_sys->i_mtu
 *****************************************************************************/
469
static block_t *NewUDPPacket( sout_access_out_t *p_access, mtime_t i_dts)
gbazin's avatar
   
gbazin committed
470
471
{
    sout_access_out_sys_t *p_sys = p_access->p_sys;
472
    block_t *p_buffer;
gbazin's avatar
   
gbazin committed
473

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
    vlc_mutex_lock( &p_sys->p_thread->blocks_lock );
    while ( p_sys->p_thread->i_empty_depth > MAX_EMPTY_BLOCKS )
    {
        p_buffer = p_sys->p_thread->p_empty_blocks;
        p_sys->p_thread->p_empty_blocks =
                    p_sys->p_thread->p_empty_blocks->p_next;
        p_sys->p_thread->i_empty_depth--;
        vlc_mutex_unlock( &p_sys->p_thread->blocks_lock );
        block_Release( p_buffer );
        vlc_mutex_lock( &p_sys->p_thread->blocks_lock );
    }
    p_buffer = p_sys->p_thread->p_empty_blocks;
    if ( p_buffer != NULL )
    {
        p_sys->p_thread->p_empty_blocks =
                    p_sys->p_thread->p_empty_blocks->p_next;
        p_sys->p_thread->i_empty_depth--;
        vlc_mutex_unlock( &p_sys->p_thread->blocks_lock );
        p_buffer->p_next = NULL;
        p_buffer->i_flags = 0;
        p_buffer = block_Realloc( p_buffer, 0, p_sys->i_mtu );
    }
    else
    {
        vlc_mutex_unlock( &p_sys->p_thread->blocks_lock );
        p_buffer = block_New( p_access->p_sout, p_sys->i_mtu );
    }

gbazin's avatar
   
gbazin committed
502
    p_buffer->i_dts = i_dts;
503
    p_buffer->i_buffer = 0;
gbazin's avatar
   
gbazin committed
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526

    if( p_sys->b_rtpts )
    {
        mtime_t i_timestamp = p_buffer->i_dts * 9 / 100;

        /* add rtp/ts header */
        p_buffer->p_buffer[0] = 0x80;
        p_buffer->p_buffer[1] = 0x21; // mpeg2-ts

        p_buffer->p_buffer[2] = ( p_sys->i_sequence_number >> 8 )&0xff;
        p_buffer->p_buffer[3] = p_sys->i_sequence_number&0xff;
        p_sys->i_sequence_number++;

        p_buffer->p_buffer[4] = ( i_timestamp >> 24 )&0xff;
        p_buffer->p_buffer[5] = ( i_timestamp >> 16 )&0xff;
        p_buffer->p_buffer[6] = ( i_timestamp >>  8 )&0xff;
        p_buffer->p_buffer[7] = i_timestamp&0xff;

        p_buffer->p_buffer[ 8] = ( p_sys->i_ssrc >> 24 )&0xff;
        p_buffer->p_buffer[ 9] = ( p_sys->i_ssrc >> 16 )&0xff;
        p_buffer->p_buffer[10] = ( p_sys->i_ssrc >>  8 )&0xff;
        p_buffer->p_buffer[11] = p_sys->i_ssrc&0xff;

527
        p_buffer->i_buffer = 12;
gbazin's avatar
   
gbazin committed
528
529
530
531
532
    }

    return p_buffer;
}

533
534
535
536
537
538
/*****************************************************************************
 * ThreadWrite: Write a packet on the network at the good time.
 *****************************************************************************/
static void ThreadWrite( vlc_object_t *p_this )
{
    sout_access_thread_t *p_thread = (sout_access_thread_t*)p_this;
Laurent Aimar's avatar
Laurent Aimar committed
539
    mtime_t              i_date_last = -1;
540
    mtime_t              i_to_send = p_thread->i_group;
gbazin's avatar
gbazin committed
541
    int                  i_dropped_packets = 0;
542
543
544
545
#if defined(WIN32) || defined(UNDER_CE)
    char strerror_buf[WINSOCK_STRERROR_SIZE];
# define strerror( x ) winsock_strerror( strerror_buf )
#endif
gbazin's avatar
   
gbazin committed
546

547
    while( !p_thread->b_die )
548
    {
549
        block_t *p_pk;
550
        mtime_t       i_date, i_sent;
551

552
        p_pk = block_FifoGet( p_thread->p_fifo );
553

554
        i_date = p_thread->i_caching + p_pk->i_dts;
Laurent Aimar's avatar
Laurent Aimar committed
555
        if( i_date_last > 0 )
556
        {
Laurent Aimar's avatar
Laurent Aimar committed
557
558
            if( i_date - i_date_last > 2000000 )
            {
gbazin's avatar
gbazin committed
559
                if( !i_dropped_packets )
560
561
                    msg_Dbg( p_thread, "mmh, hole ("I64Fd" > 2s) -> drop",
                             i_date - i_date_last );
Laurent Aimar's avatar
Laurent Aimar committed
562

563
564
565
566
567
                vlc_mutex_lock( &p_thread->blocks_lock );
                p_pk->p_next = p_thread->p_empty_blocks;
                p_thread->p_empty_blocks = p_pk;
                p_thread->i_empty_depth++;
                vlc_mutex_unlock( &p_thread->blocks_lock );
Laurent Aimar's avatar
Laurent Aimar committed
568
569

                i_date_last = i_date;
gbazin's avatar
gbazin committed
570
                i_dropped_packets++;
Laurent Aimar's avatar
Laurent Aimar committed
571
572
                continue;
            }
573
            else if( i_date - i_date_last < -1000 )
574
            {
575
576
577
                if( !i_dropped_packets )
                    msg_Dbg( p_thread, "mmh, packets in the past ("I64Fd")",
                             i_date_last - i_date );
578
            }
579
580
        }

581
        i_to_send--;
582
        if( !i_to_send || (p_pk->i_flags & BLOCK_FLAG_CLOCK) )
583
584
585
586
        {
            mwait( i_date );
            i_to_send = p_thread->i_group;
        }
587
588
589
590
591
        if( send( p_thread->i_handle, p_pk->p_buffer, p_pk->i_buffer, 0 )
              == -1 )
        {
            msg_Warn( p_thread, "send error: %s", strerror(errno) );
        }
592

gbazin's avatar
gbazin committed
593
594
595
596
597
598
        if( i_dropped_packets )
        {
            msg_Dbg( p_thread, "dropped %i packets", i_dropped_packets );
            i_dropped_packets = 0;
        }

599
#if 1
600
601
602
603
604
605
        i_sent = mdate();
        if ( i_sent > i_date + 20000 )
        {
            msg_Dbg( p_thread, "packet has been sent too late (" I64Fd ")",
                     i_sent - i_date );
        }
gbazin's avatar
gbazin committed
606
607
#endif

608
609
610
611
612
613
        vlc_mutex_lock( &p_thread->blocks_lock );
        p_pk->p_next = p_thread->p_empty_blocks;
        p_thread->p_empty_blocks = p_pk;
        p_thread->i_empty_depth++;
        vlc_mutex_unlock( &p_thread->blocks_lock );

Laurent Aimar's avatar
Laurent Aimar committed
614
        i_date_last = i_date;
615
616
    }
}