udp.c 23 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin committed
2
 * udp.c: raw UDP & RTP input module
3
 *****************************************************************************
4
 * Copyright (C) 2001-2005 the VideoLAN team
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
5
 * Copyright (C) 2007 Remi Denis-Courmont
hartman's avatar
hartman committed
6
 * $Id$
7
8
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
9
 *          Tristan Leteurtre <tooney@via.ecp.fr>
Laurent Aimar's avatar
Laurent Aimar committed
10
 *          Laurent Aimar <fenrir@via.ecp.fr>
11
 *          Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
12
 *          Remi Denis-Courmont
13
 *
Jean-Paul Saman's avatar
Jean-Paul Saman committed
14
 * Reviewed: 23 October 2003, Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
Jean-Paul Saman's avatar
Jean-Paul Saman committed
15
 *
16
17
18
19
 * 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.
Laurent Aimar's avatar
Laurent Aimar committed
20
 *
21
22
23
24
25
26
27
 * 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
dionoea's avatar
dionoea committed
28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
29
30
31
32
33
34
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

35
36
37
38
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

39
#include <vlc_common.h>
40
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
41
42
#include <vlc_access.h>
#include <vlc_network.h>
43

44
45
46
47
48
49
50
51
52
53
#ifndef SOCK_DCCP /* provisional API */
# ifdef __linux__
#  define SOCK_DCCP 6
# endif
#endif

#ifndef IPPROTO_DCCP
# define IPPROTO_DCCP 33 /* IANA */
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
54
55
56
57
#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136 /* from IANA */
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
58
#define MTU 65535
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
59

60
61
62
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
63
#define CACHING_TEXT N_("Caching value in ms")
gbazin's avatar
   
gbazin committed
64
#define CACHING_LONGTEXT N_( \
65
    "Caching value for UDP streams. This " \
zorglub's avatar
zorglub committed
66
    "value should be set in milliseconds." )
gbazin's avatar
   
gbazin committed
67

68
#define RTP_LATE_TEXT N_("RTP reordering timeout in ms")
69
#define RTP_LATE_LONGTEXT N_( \
zorglub's avatar
zorglub committed
70
71
    "VLC reorders RTP packets. The input will wait for late packets at most "\
    "the time specified here (in milliseconds)." )
72

Laurent Aimar's avatar
Laurent Aimar committed
73
74
75
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

76
vlc_module_begin();
77
78
    set_shortname( N_("UDP/RTP" ) );
    set_description( N_("UDP/RTP input") );
zorglub's avatar
zorglub committed
79
80
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACCESS );
gbazin's avatar
   
gbazin committed
81
82

    add_integer( "udp-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT,
83
84
                 CACHING_LONGTEXT, true );
    add_integer( "rtp-late", 100, NULL, RTP_LATE_TEXT, RTP_LATE_LONGTEXT, true );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
85
    add_obsolete_bool( "udp-auto-mtu" );
gbazin's avatar
   
gbazin committed
86

87
    set_capability( "access", 0 );
88
    add_shortcut( "udp" );
89
90
91
    add_shortcut( "udpstream" );
    add_shortcut( "udp4" );
    add_shortcut( "udp6" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
92
    add_shortcut( "udplite" );
93
    add_shortcut( "rtptcp" ); /* tcp name is already taken */
94
    add_shortcut( "dccp" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
95

96
    set_callbacks( Open, Close );
97
98
vlc_module_end();

Laurent Aimar's avatar
Laurent Aimar committed
99
100
101
102
103
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
#define RTP_HEADER_LEN 12

104
static block_t *BlockUDP( access_t * );
105
static block_t *BlockStartRTP( access_t * );
106
107
108
static block_t *BlockRTP( access_t * );
static block_t *BlockChoose( access_t * );
static int Control( access_t *, int, va_list );
Laurent Aimar's avatar
Laurent Aimar committed
109
110
111
112

struct access_sys_t
{
    int fd;
113

114
    bool b_framed_rtp, b_comedia;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
115

116
117
    /* reorder rtp packets when out-of-sequence */
    uint16_t i_last_seqno;
118
    mtime_t i_rtp_late;
119
120
    block_t *p_list;
    block_t *p_end;
121
    block_t *p_partial_frame; /* Partial Framed RTP packet */
Laurent Aimar's avatar
Laurent Aimar committed
122
123
};

124
125
126
127
128
/*****************************************************************************
 * Open: open the socket
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
129
130
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
Laurent Aimar's avatar
Laurent Aimar committed
131

132
    char *psz_name = strdup( p_access->psz_path );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
133
134
    char *psz_parser;
    const char *psz_server_addr, *psz_bind_addr = "";
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
135
    int  i_bind_port, i_server_port = 0;
136
    int fam = AF_UNSPEC, proto = IPPROTO_UDP;
137

138
139
    /* Set up p_access */
    access_InitFields( p_access );
140
    ACCESS_SET_CALLBACKS( NULL, BlockStartRTP, Control, NULL );
141
    p_access->info.b_prebuffered = false;
142
143
144
145
    MALLOC_ERR( p_access->p_sys, access_sys_t ); p_sys = p_access->p_sys;
    memset (p_sys, 0, sizeof (*p_sys));

    if (strlen (p_access->psz_access) > 0)
146
    {
147
        switch (p_access->psz_access[strlen (p_access->psz_access) - 1])
148
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
149
150
151
            case '4':
                fam = AF_INET;
                break;
Laurent Aimar's avatar
Laurent Aimar committed
152

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
153
154
155
            case '6':
                fam = AF_INET6;
                break;
156
        }
157
158

        if (strcmp (p_access->psz_access, "udplite") == 0)
159
            proto = IPPROTO_UDPLITE;
160
        else
161
162
163
164
        if (strncmp (p_access->psz_access, "udp", 3 ) == 0 )
            p_access->pf_block = BlockChoose;
        else
        if (strcmp (p_access->psz_access, "rtptcp") == 0)
165
            proto = IPPROTO_TCP;
166
167
168
        else
        if (strcmp (p_access->psz_access, "dccp") == 0)
            proto = IPPROTO_DCCP;
169
    }
170

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
171
172
    i_bind_port = var_CreateGetInteger( p_access, "server-port" );

173
174
    /* Parse psz_name syntax :
     * [serveraddr[:serverport]][@[bindaddr]:[bindport]] */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
175
176
    psz_parser = strchr( psz_name, '@' );
    if( psz_parser != NULL )
177
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
178
179
180
        /* Found bind address and/or bind port */
        *psz_parser++ = '\0';
        psz_bind_addr = psz_parser;
181

182
        if( psz_bind_addr[0] == '[' )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
183
184
            /* skips bracket'd IPv6 address */
            psz_parser = strchr( psz_parser, ']' );
185

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
186
        if( psz_parser != NULL )
187
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
188
189
            psz_parser = strchr( psz_parser, ':' );
            if( psz_parser != NULL )
190
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
191
192
                *psz_parser++ = '\0';
                i_bind_port = atoi( psz_parser );
193
194
195
196
            }
        }
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
197
    psz_server_addr = psz_name;
198
199
200
    psz_parser = ( psz_server_addr[0] == '[' )
        ? strchr( psz_name, ']' ) /* skips bracket'd IPv6 address */
        : psz_name;
201

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
202
203
    if( psz_parser != NULL )
    {
204
        psz_parser = strchr( psz_parser, ':' );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
205
        if( psz_parser != NULL )
206
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
207
208
            *psz_parser++ = '\0';
            i_server_port = atoi( psz_parser );
209
210
211
        }
    }

212
    msg_Dbg( p_access, "opening server=%s:%d local=%s:%d",
213
214
             psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );

215
    /* Hmm, the net_* connection functions may need to be unified... */
216
217
218
219
220
221
222
223
224
225
    switch (proto)
    {
        case IPPROTO_UDP:
        case IPPROTO_UDPLITE:
            p_sys->fd = net_OpenDgram( p_access, psz_bind_addr, i_bind_port,
                                       psz_server_addr, i_server_port, fam,
                                       proto );
            break;

        case IPPROTO_TCP:
226
            p_sys->fd = net_ConnectTCP( p_access, psz_server_addr, i_server_port );
227
            p_access->pf_block = BlockRTP;
228
            p_sys->b_comedia = p_sys->b_framed_rtp = true;
229
230
231
232
            break;

        case IPPROTO_DCCP:
#ifdef SOCK_DCCP
233
234
            var_Create( p_access, "dccp-service", VLC_VAR_STRING );
            var_SetString( p_access, "dccp-service", "RTPV" );
235
236
237
238
239
240
            p_sys->fd = net_Connect( p_access, psz_server_addr, i_server_port,
                                     SOCK_DCCP, IPPROTO_DCCP );
#else
            p_sys->fd = -1;
            msg_Err( p_access, "DCCP support not compiled-in!" );
#endif
241
            p_sys->b_comedia = true;
242
243
            break;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
244
245
    free (psz_name);
    if( p_sys->fd == -1 )
246
    {
247
        msg_Err( p_access, "cannot open socket" );
Laurent Aimar's avatar
Laurent Aimar committed
248
249
        free( p_sys );
        return VLC_EGENERIC;
250
251
    }

252
    shutdown( p_sys->fd, SHUT_WR );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
253
    net_SetCSCov (p_sys->fd, -1, 12);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
254

gbazin's avatar
   
gbazin committed
255
    /* Update default_pts to a suitable value for udp access */
256
    var_Create( p_access, "udp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
gbazin's avatar
   
gbazin committed
257

258
259
260
    /* RTP reordering for out-of-sequence packets */
    p_sys->i_rtp_late = var_CreateGetInteger( p_access, "rtp-late" ) * 1000;
    p_sys->i_last_seqno = 0;
261
262
    p_sys->p_list = NULL;
    p_sys->p_end = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
263
    return VLC_SUCCESS;
264
}
265
266
267
268
269
270

/*****************************************************************************
 * Close: free unused data structures
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
271
272
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys = p_access->p_sys;
273

274
    block_ChainRelease( p_sys->p_list );
Laurent Aimar's avatar
Laurent Aimar committed
275
276
    net_Close( p_sys->fd );
    free( p_sys );
277
278
279
}

/*****************************************************************************
280
 * Control:
281
 *****************************************************************************/
282
static int Control( access_t *p_access, int i_query, va_list args )
283
{
284
    bool   *pb_bool;
285
286
287
288
289
290
291
292
293
294
    int          *pi_int;
    int64_t      *pi_64;

    switch( i_query )
    {
        /* */
        case ACCESS_CAN_SEEK:
        case ACCESS_CAN_FASTSEEK:
        case ACCESS_CAN_PAUSE:
        case ACCESS_CAN_CONTROL_PACE:
295
296
            pb_bool = (bool*)va_arg( args, bool* );
            *pb_bool = false;
297
298
299
300
            break;
        /* */
        case ACCESS_GET_MTU:
            pi_int = (int*)va_arg( args, int * );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301
            *pi_int = MTU;
302
303
304
305
            break;

        case ACCESS_GET_PTS_DELAY:
            pi_64 = (int64_t*)va_arg( args, int64_t * );
306
            *pi_64 = var_GetInteger( p_access, "udp-caching" ) * 1000;
307
308
309
310
311
312
313
            break;

        /* */
        case ACCESS_SET_PAUSE_STATE:
        case ACCESS_GET_TITLE_INFO:
        case ACCESS_SET_TITLE:
        case ACCESS_SET_SEEKPOINT:
314
        case ACCESS_SET_PRIVATE_ID_STATE:
315
        case ACCESS_GET_CONTENT_TYPE:
316
317
318
            return VLC_EGENERIC;

        default:
319
            msg_Warn( p_access, "unimplemented query in control" );
320
            return VLC_EGENERIC;
321

322
323
    }
    return VLC_SUCCESS;
324
325
}

326
/*****************************************************************************
327
 * BlockUDP:
328
 *****************************************************************************/
329
static block_t *BlockUDP( access_t *p_access )
330
{
331
332
    access_sys_t *p_sys = p_access->p_sys;
    block_t      *p_block;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
333
    ssize_t len;
334

335
336
337
    if( p_access->info.b_eof )
        return NULL;

338
    /* Read data */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
339
    p_block = block_New( p_access, MTU );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340
    len = net_Read( p_access, p_sys->fd, NULL,
341
                    p_block->p_buffer, MTU, false );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
342
343
    if( ( len < 0 )
     || ( p_sys->b_comedia && ( len == 0 ) ) )
344
    {
345
346
        if( p_sys->b_comedia )
        {
347
            p_access->info.b_eof = true;
348
349
            msg_Dbg( p_access, "connection-oriented media hangup" );
        }
350
351
352
        block_Release( p_block );
        return NULL;
    }
353

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
354
    return block_Realloc( p_block, 0, p_block->i_buffer = len );
355
356
}

357
/*****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
358
 * BlockTCP: Framed RTP/AVP packet reception for COMEDIA (see RFC4571)
359
360
361
362
363
 *****************************************************************************/
static block_t *BlockTCP( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
    block_t      *p_block = p_sys->p_partial_frame;
364
365
366

    if( p_access->info.b_eof )
        return NULL;
367
368
369
370

    if( p_block == NULL )
    {
        /* MTU should always be 65535 in this case */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
371
        p_sys->p_partial_frame = p_block = block_New( p_access, 2 + MTU );
372
373
374
375
376
377
378
        if (p_block == NULL)
            return NULL;
    }

    /* Read RTP framing */
    if (p_block->i_buffer < 2)
    {
379
380
        int i_read = net_Read( p_access, p_sys->fd, NULL,
                               p_block->p_buffer + p_block->i_buffer,
381
                               2 - p_block->i_buffer, false );
382
383
384
385
386
387
388
389
390
391
392
393
        if( i_read <= 0 )
            goto error;

        p_block->i_buffer += i_read;
        if (p_block->i_buffer < 2)
            return NULL;
    }

    uint16_t framelen = GetWLE( p_block->p_buffer );
    /* Read RTP frame */
    if( framelen > 0 )
    {
394
395
        int i_read = net_Read( p_access, p_sys->fd, NULL,
                               p_block->p_buffer + p_block->i_buffer,
396
                               2 + framelen - p_block->i_buffer, false );
397
398
399
400
401
402
        if( i_read <= 0 )
            goto error;

        p_block->i_buffer += i_read;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
403
    if( p_block->i_buffer < (2u + framelen) )
404
405
406
407
408
409
410
411
412
        return NULL; // incomplete frame

    /* Hide framing from RTP layer */
    p_block->p_buffer += 2;
    p_block->i_buffer -= 2;
    p_sys->p_partial_frame = NULL;
    return p_block;

error:
413
    p_access->info.b_eof = true;
414
415
416
417
418
419
    block_Release( p_block );
    p_sys->p_partial_frame = NULL;
    return NULL;
}


420
421
422
423
/*
 * rtp_ChainInsert - insert a p_block in the chain and
 * look at the sequence numbers.
 */
424
static inline bool rtp_ChainInsert( access_t *p_access, block_t *p_block )
425
{
Jean-Paul Saman's avatar
Jean-Paul Saman committed
426
    access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
427
428
    block_t *p_prev = NULL;
    block_t *p = p_sys->p_end;
429
    uint16_t i_new = (uint16_t) p_block->i_dts;
430
    uint16_t i_tmp = 0;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
431

432
    if( !p_sys->p_list )
433
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
434
        p_sys->p_list = p_block;
435
        p_sys->p_end = p_block;
436
        return true;
437
    }
438
    /* walk through the queue from top down since the new packet is in
439
440
441
    most cases just appended to the end */

    for( ;; )
442
    {
443
        i_tmp = i_new - (uint16_t) p->i_dts;
444

445
        if( !i_tmp )   /* trash duplicate */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
446
            break;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
447

448
        if ( i_tmp < 32768 )
449
        {   /* insert after this block ( i_new > p->i_dts ) */
450
            p_block->p_next = p->p_next;
451
            p->p_next = p_block;
452
453
            p_block->p_prev = p;
            if (p_prev)
454
            {
455
                p_prev->p_prev = p_block;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
456
457
                msg_Dbg(p_access, "RTP reordering: insert after %d, new %d",
                        (uint16_t) p->i_dts, i_new );
458
            }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
459
            else
460
            {
461
                p_sys->p_end = p_block;
462
            }
463
            return true;
464
        }
465
466
467
468
469
        if( p == p_sys->p_list )
        {   /* we've reached bottom of chain */
            i_tmp = p_sys->i_last_seqno - i_new;
            if( !p_access->info.b_prebuffered || (i_tmp > 32767) )
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
470
                msg_Dbg(p_access, "RTP reordering: prepend %d before %d",
471
                        i_new, (uint16_t) p->i_dts );
472
473
474
                p_block->p_next = p;
                p->p_prev = p_block;
                p_sys->p_list = p_block;
475
                return true;
476
            }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
477

478
            if( !i_tmp )   /* trash duplicate */
479
                break;
480
481
482

            /* reordering failed - append the packet to the end of queue */
            msg_Dbg(p_access, "RTP: sequence changed (or buffer too small) "
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
483
                    "new: %d, buffer %d...%d", i_new, (uint16_t) p->i_dts,
484
                (uint16_t) p_sys->p_end->i_dts);
485
486
487
            p_sys->p_end->p_next = p_block;
            p_block->p_prev = p_sys->p_end;
            p_sys->p_end = p_block;
488
            return true;
489
490
491
        }
        p_prev = p;
        p = p->p_prev;
492
    }
493
    block_Release( p_block );
494
    return false;
495
496
}

497
/*****************************************************************************
498
 * BlockParseRTP: decapsulate the RTP packet and return it
499
500
501
 *****************************************************************************/
static block_t *BlockParseRTP( access_t *p_access, block_t *p_block )
{
502
    int      i_payload_type;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
503
    size_t   i_skip = RTP_HEADER_LEN;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
504

505
506
    if( p_block == NULL )
        return NULL;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
507

508
    if( p_block->i_buffer < RTP_HEADER_LEN )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
509
510
    {
        msg_Dbg( p_access, "short RTP packet received" );
511
        goto trash;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
512
    }
513

514
    /* Parse the header and make some verifications.
515
     * See RFC 3550. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
516
517
518
519
520
521
522
523
524
525
    // Version number:
    if( ( p_block->p_buffer[0] >> 6 ) != 2)
    {
        msg_Dbg( p_access, "RTP version is %u instead of 2",
                 p_block->p_buffer[0] >> 6 );
        goto trash;
    }
    // Padding bit:
    uint8_t pad = (p_block->p_buffer[0] & 0x20)
                    ? p_block->p_buffer[p_block->i_buffer - 1] : 0;
526
527
    // CSRC count:
    i_skip += (p_block->p_buffer[0] & 0x0F) * 4;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
528
    // Extension header:
529
    if (p_block->p_buffer[0] & 0x10) /* Extension header */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
530
531
532
533
534
    {
        i_skip += 4;
        if ((size_t)p_block->i_buffer < i_skip)
            goto trash;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
535
        i_skip += 4 * GetWBE( p_block->p_buffer + i_skip - 2 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
536
537
    }

538
    i_payload_type    = p_block->p_buffer[1] & 0x7F;
539

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
540
541
542
    /* Remember sequence number in i_dts */
    p_block->i_pts = mdate();
    p_block->i_dts = (mtime_t) GetWBE( p_block->p_buffer + 2 );
543

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
544
    /* FIXME: use rtpmap */
545
546
    const char *psz_demux = NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
547
    switch( i_payload_type )
548
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
549
550
        case 14: // MPA: MPEG Audio (RFC2250, §3.4)
            i_skip += 4; // 32 bits RTP/MPA header
551
            psz_demux = "mpga";
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
552
553
554
555
556
557
558
559
560
561
562
            break;

        case 32: // MPV: MPEG Video (RFC2250, §3.5)
            i_skip += 4; // 32 bits RTP/MPV header
            if( (size_t)p_block->i_buffer < i_skip )
                goto trash;
            if( p_block->p_buffer[i_skip - 3] & 0x4 )
            {
                /* MPEG2 Video extension header */
                /* TODO: shouldn't we skip this too ? */
            }
563
            psz_demux = "mpgv";
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
564
565
566
567
            break;

        case 33: // MP2: MPEG TS (RFC2250, §2)
            /* plain TS over RTP */
568
            psz_demux = "ts";
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
569
570
            break;

571
572
573
574
575
576
577
        case 72: /* muxed SR */
        case 73: /* muxed RR */
        case 74: /* muxed SDES */
        case 75: /* muxed BYE */
        case 76: /* muxed APP */
            goto trash; /* ooh! ignoring RTCP is evil! */

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
578
579
        default:
            msg_Dbg( p_access, "unsupported RTP payload type: %u", i_payload_type );
580
581
            goto trash;
    }
582

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
583
    if( (size_t)p_block->i_buffer < (i_skip + pad) )
584
        goto trash;
585

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
586
    /* Remove the RTP header */
587
588
    p_block->i_buffer -= i_skip;
    p_block->p_buffer += i_skip;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
589
590
591
592
593

    /* This is the place for deciphering and authentication */

    /* Remove padding (at the end) */
    p_block->i_buffer -= pad;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
594

595
596
597
598
599
600
601
602
603
#if 0
    /* Emulate packet loss */
    if ( (i_sequence_number % 4000) == 0)
    {
        msg_Warn( p_access, "Emulating packet drop" );
        block_Release( p_block );
        return NULL;
    }
#endif
Jean-Paul Saman's avatar
Jean-Paul Saman committed
604

605
606
607
608
609
610
    if( !p_access->psz_demux || !*p_access->psz_demux )
    {
        free( p_access->psz_demux );
        p_access->psz_demux = strdup( psz_demux );
    }

611
    return p_block;
612

613
614
615
616
617
trash:
    block_Release( p_block );
    return NULL;
}

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
/*****************************************************************************
 * BlockRTP: receives an RTP packet, parses it, queues it queue,
 * then dequeues the oldest packet and returns it to input/demux.
 ****************************************************************************/
static block_t *BlockRTP( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
    block_t *p;

    while ( !p_sys->p_list ||
             ( mdate() - p_sys->p_list->i_pts ) < p_sys->i_rtp_late )
    {
        p = BlockParseRTP( p_access,
                           p_sys->b_framed_rtp ? BlockTCP( p_access )
                                               : BlockUDP( p_access ) );
        if ( !p )
            return NULL;

        rtp_ChainInsert( p_access, p );
    }

    p = p_sys->p_list;
    p_sys->p_list = p_sys->p_list->p_next;
    p_sys->i_last_seqno++;
    if( p_sys->i_last_seqno != (uint16_t) p->i_dts )
    {
        msg_Dbg( p_access, "RTP: packet(s) lost, expected %d, got %d",
                 p_sys->i_last_seqno, (uint16_t) p->i_dts );
        p_sys->i_last_seqno = (uint16_t) p->i_dts;
    }
    p->p_next = NULL;
    return p;
}

652
653
654
655
656
/*****************************************************************************
 * BlockPrebufferRTP: waits until we have at least two RTP datagrams,
 * so that we can synchronize the RTP sequence number.
 * This is only useful for non-reliable transport protocols.
 ****************************************************************************/
657
658
659
static block_t *BlockPrebufferRTP( access_t *p_access, block_t *p_block )
{
    access_sys_t *p_sys = p_access->p_sys;
Marian Durkovic's avatar
Marian Durkovic committed
660
    mtime_t   i_first = mdate();
661
662
663
    int       i_count = 0;
    block_t   *p = p_block;

664
665
666
    if( BlockParseRTP( p_access, p_block ) == NULL )
        return NULL;

667
    for( ;; )
668
    {
Marian Durkovic's avatar
Marian Durkovic committed
669
        mtime_t i_date = mdate();
670
671
672
673

        if( p && rtp_ChainInsert( p_access, p ))
            i_count++;

Marian Durkovic's avatar
Marian Durkovic committed
674
675
        /* Require at least 2 packets in the buffer */
        if( i_count > 2 && (i_date - i_first) > p_sys->i_rtp_late )
676
677
            break;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
678
        p = BlockParseRTP( p_access, BlockUDP( p_access ) );
679
        if( !p && (i_date - i_first) > p_sys->i_rtp_late )
680
        {
681
            msg_Err( p_access, "error in RTP prebuffering!" );
682
            return NULL;
683
684
        }
    }
685

686
    msg_Dbg( p_access, "RTP: prebuffered %d packets", i_count - 1 );
687
    p_access->info.b_prebuffered = true;
688
689
    p = p_sys->p_list;
    p_sys->p_list = p_sys->p_list->p_next;
690
    p_sys->i_last_seqno = (uint16_t) p->i_dts;
691
692
    p->p_next = NULL;
    return p;
693
}
694

695
static block_t *BlockStartRTP( access_t *p_access )
696
{
697
    p_access->pf_block = BlockRTP;
698
    return BlockPrebufferRTP( p_access, BlockUDP( p_access ) );
699
700
}

701

702
/*****************************************************************************
703
 * BlockChoose: decide between RTP and UDP
704
 *****************************************************************************/
705
static block_t *BlockChoose( access_t *p_access )
706
{
707
708
709
    block_t *p_block;
    int     i_rtp_version;
    int     i_payload_type;
710

711
712
    if( ( p_block = BlockUDP( p_access ) ) == NULL )
        return NULL;
713

714
    if( p_block->p_buffer[0] == 0x47 )
715
    {
716
717
        msg_Dbg( p_access, "detected TS over raw UDP" );
        p_access->pf_block = BlockUDP;
718
        p_access->info.b_prebuffered = true;
719
        return p_block;
720
721
    }

722
723
724
    if( p_block->i_buffer < RTP_HEADER_LEN )
        return p_block;

725
    /* Parse the header and make some verifications.
726
     * See RFC 3550. */
727

728
    i_rtp_version  = p_block->p_buffer[0] >> 6;
729
    i_payload_type = ( p_block->p_buffer[1] & 0x7F );
730

731
    if( i_rtp_version != 2 )
732
    {
733
734
        msg_Dbg( p_access, "no supported RTP header detected" );
        p_access->pf_block = BlockUDP;
735
        p_access->info.b_prebuffered = true;
736
        return p_block;
737
738
    }

739
    switch( i_payload_type )
740
    {
741
        case 33:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
742
            msg_Dbg( p_access, "detected MPEG2 TS over RTP" );
Rafaël Carré's avatar
Rafaël Carré committed
743
            free( p_access->psz_demux );
744
            p_access->psz_demux = strdup( "ts" );
745
746
747
            break;

        case 14:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
748
            msg_Dbg( p_access, "detected MPEG Audio over RTP" );
Rafaël Carré's avatar
Rafaël Carré committed
749
            free( p_access->psz_demux );
750
            p_access->psz_demux = strdup( "mpga" );
751
752
753
            break;

        case 32:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
754
            msg_Dbg( p_access, "detected MPEG Video over RTP" );
Rafaël Carré's avatar
Rafaël Carré committed
755
            free( p_access->psz_demux );
756
            p_access->psz_demux = strdup( "mpgv" );
757
758
759
760
761
            break;

        default:
            msg_Dbg( p_access, "no RTP header detected" );
            p_access->pf_block = BlockUDP;
762
            p_access->info.b_prebuffered = true;
763
            return p_block;
764
765
    }

766
    p_access->pf_block = BlockRTP;
767
    return BlockPrebufferRTP( p_access, p_block );
768
}