udp.c 23.1 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin 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
Derk-Jan Hartman's avatar
Derk-Jan 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
Antoine Cellerier's avatar
Antoine Cellerier 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>
Clément Stenac's avatar
Clément Stenac 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
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
63
#define CACHING_TEXT N_("Caching value in ms")
Gildas Bazin's avatar
 
Gildas Bazin committed
64
#define CACHING_LONGTEXT N_( \
65
    "Caching value for UDP streams. This " \
Clément Stenac's avatar
Clément Stenac committed
66
    "value should be set in milliseconds." )
Gildas Bazin's avatar
 
Gildas Bazin committed
67

68
#define RTP_LATE_TEXT N_("RTP reordering timeout in ms")
69
#define RTP_LATE_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac 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") );
Clément Stenac's avatar
Clément Stenac committed
79 80
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACCESS );
Gildas Bazin's avatar
 
Gildas Bazin 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" );
Gildas Bazin's avatar
 
Gildas Bazin committed
86

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

99
    set_callbacks( Open, Close );
100 101
vlc_module_end();

Laurent Aimar's avatar
Laurent Aimar committed
102 103 104 105 106
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
#define RTP_HEADER_LEN 12

107
static block_t *BlockUDP( access_t * );
108
static block_t *BlockStartRTP( access_t * );
109 110 111
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
112 113 114 115

struct access_sys_t
{
    int fd;
116

117
    bool b_framed_rtp, b_comedia;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
118

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

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

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

141 142
    /* Set up p_access */
    access_InitFields( p_access );
143
    ACCESS_SET_CALLBACKS( NULL, BlockStartRTP, Control, NULL );
144
    p_access->info.b_prebuffered = false;
145 146 147 148
    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)
149
    {
150
        switch (p_access->psz_access[strlen (p_access->psz_access) - 1])
151
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
152 153 154
            case '4':
                fam = AF_INET;
                break;
Laurent Aimar's avatar
Laurent Aimar committed
155

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
156 157 158
            case '6':
                fam = AF_INET6;
                break;
159
        }
160 161

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

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

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

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

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

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

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

215
    msg_Dbg( p_access, "opening server=%s:%d local=%s:%d",
216 217
             psz_server_addr, i_server_port, psz_bind_addr, i_bind_port );

218
    /* Hmm, the net_* connection functions may need to be unified... */
219 220 221 222 223 224 225 226 227 228
    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:
229
            p_sys->fd = net_ConnectTCP( p_access, psz_server_addr, i_server_port );
230
            p_access->pf_block = BlockRTP;
231
            p_sys->b_comedia = p_sys->b_framed_rtp = true;
232 233 234 235
            break;

        case IPPROTO_DCCP:
#ifdef SOCK_DCCP
236 237
            var_Create( p_access, "dccp-service", VLC_VAR_STRING );
            var_SetString( p_access, "dccp-service", "RTPV" );
238 239 240 241 242 243
            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
244
            p_sys->b_comedia = true;
245 246
            break;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
247 248
    free (psz_name);
    if( p_sys->fd == -1 )
249
    {
250
        msg_Err( p_access, "cannot open socket" );
Laurent Aimar's avatar
Laurent Aimar committed
251 252
        free( p_sys );
        return VLC_EGENERIC;
253 254
    }

255
    shutdown( p_sys->fd, SHUT_WR );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
256
    net_SetCSCov (p_sys->fd, -1, 12);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
257

Gildas Bazin's avatar
 
Gildas Bazin committed
258
    /* Update default_pts to a suitable value for udp access */
259
    var_Create( p_access, "udp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
Gildas Bazin's avatar
 
Gildas Bazin committed
260

261 262 263
    /* 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;
264 265
    p_sys->p_list = NULL;
    p_sys->p_end = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
266
    return VLC_SUCCESS;
267
}
268 269 270 271 272 273

/*****************************************************************************
 * Close: free unused data structures
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
274 275
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys = p_access->p_sys;
276

277
    block_ChainRelease( p_sys->p_list );
Laurent Aimar's avatar
Laurent Aimar committed
278 279
    net_Close( p_sys->fd );
    free( p_sys );
280 281 282
}

/*****************************************************************************
283
 * Control:
284
 *****************************************************************************/
285
static int Control( access_t *p_access, int i_query, va_list args )
286
{
287
    bool   *pb_bool;
288 289 290 291 292 293 294 295 296 297
    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:
298 299
            pb_bool = (bool*)va_arg( args, bool* );
            *pb_bool = false;
300 301 302 303
            break;
        /* */
        case ACCESS_GET_MTU:
            pi_int = (int*)va_arg( args, int * );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
304
            *pi_int = MTU;
305 306 307 308
            break;

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

        /* */
        case ACCESS_SET_PAUSE_STATE:
        case ACCESS_GET_TITLE_INFO:
        case ACCESS_SET_TITLE:
        case ACCESS_SET_SEEKPOINT:
317
        case ACCESS_SET_PRIVATE_ID_STATE:
318
        case ACCESS_GET_CONTENT_TYPE:
319 320 321
            return VLC_EGENERIC;

        default:
322
            msg_Warn( p_access, "unimplemented query in control" );
323
            return VLC_EGENERIC;
324

325 326
    }
    return VLC_SUCCESS;
327 328
}

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

338 339 340
    if( p_access->info.b_eof )
        return NULL;

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
357
    return block_Realloc( p_block, 0, p_block->i_buffer = len );
358 359
}

360
/*****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
361
 * BlockTCP: Framed RTP/AVP packet reception for COMEDIA (see RFC4571)
362 363 364 365 366
 *****************************************************************************/
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;
367 368 369

    if( p_access->info.b_eof )
        return NULL;
370 371 372 373

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

    /* Read RTP framing */
    if (p_block->i_buffer < 2)
    {
382 383
        int i_read = net_Read( p_access, p_sys->fd, NULL,
                               p_block->p_buffer + p_block->i_buffer,
384
                               2 - p_block->i_buffer, false );
385 386 387 388 389 390 391 392 393 394 395 396
        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 )
    {
397 398
        int i_read = net_Read( p_access, p_sys->fd, NULL,
                               p_block->p_buffer + p_block->i_buffer,
399
                               2 + framelen - p_block->i_buffer, false );
400 401 402 403 404 405
        if( i_read <= 0 )
            goto error;

        p_block->i_buffer += i_read;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
406
    if( p_block->i_buffer < (2u + framelen) )
407 408 409 410 411 412 413 414 415
        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:
416
    p_access->info.b_eof = true;
417 418 419 420 421 422
    block_Release( p_block );
    p_sys->p_partial_frame = NULL;
    return NULL;
}


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

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

    for( ;; )
445
    {
446
        i_tmp = i_new - (uint16_t) p->i_dts;
447

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

451
        if ( i_tmp < 32768 )
452
        {   /* insert after this block ( i_new > p->i_dts ) */
453
            p_block->p_next = p->p_next;
454
            p->p_next = p_block;
455 456
            p_block->p_prev = p;
            if (p_prev)
457
            {
458
                p_prev->p_prev = p_block;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
459 460
                msg_Dbg(p_access, "RTP reordering: insert after %d, new %d",
                        (uint16_t) p->i_dts, i_new );
461
            }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
462
            else
463
            {
464
                p_sys->p_end = p_block;
465
            }
466
            return true;
467
        }
468 469 470 471 472
        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
473
                msg_Dbg(p_access, "RTP reordering: prepend %d before %d",
474
                        i_new, (uint16_t) p->i_dts );
475 476 477
                p_block->p_next = p;
                p->p_prev = p_block;
                p_sys->p_list = p_block;
478
                return true;
479
            }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
480

481
            if( !i_tmp )   /* trash duplicate */
482
                break;
483 484 485

            /* 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
486
                    "new: %d, buffer %d...%d", i_new, (uint16_t) p->i_dts,
487
                (uint16_t) p_sys->p_end->i_dts);
488 489 490
            p_sys->p_end->p_next = p_block;
            p_block->p_prev = p_sys->p_end;
            p_sys->p_end = p_block;
491
            return true;
492 493 494
        }
        p_prev = p;
        p = p->p_prev;
495
    }
496
    block_Release( p_block );
497
    return false;
498 499
}

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

508 509
    if( p_block == NULL )
        return NULL;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
510

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

517
    /* Parse the header and make some verifications.
518
     * See RFC 3550. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
519 520 521 522 523 524 525 526 527 528
    // 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;
529 530
    // CSRC count:
    i_skip += (p_block->p_buffer[0] & 0x0F) * 4;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
531
    // Extension header:
532
    if (p_block->p_buffer[0] & 0x10) /* Extension header */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
533 534 535 536 537
    {
        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
538
        i_skip += 4 * GetWBE( p_block->p_buffer + i_skip - 2 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
539 540
    }

541
    i_payload_type    = p_block->p_buffer[1] & 0x7F;
542

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
547
    /* FIXME: use rtpmap */
548 549
    const char *psz_demux = NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
550
    switch( i_payload_type )
551
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
552 553
        case 14: // MPA: MPEG Audio (RFC2250, §3.4)
            i_skip += 4; // 32 bits RTP/MPA header
554
            psz_demux = "mpga";
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
555 556 557 558 559 560 561 562 563 564 565
            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 ? */
            }
566
            psz_demux = "mpgv";
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
567 568 569 570
            break;

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

574 575 576 577 578 579 580
        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
581 582
        default:
            msg_Dbg( p_access, "unsupported RTP payload type: %u", i_payload_type );
583 584
            goto trash;
    }
585

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

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

    /* 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
597

598 599 600 601 602 603 604 605 606
#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
607

608 609 610 611 612 613
    if( !p_access->psz_demux || !*p_access->psz_demux )
    {
        free( p_access->psz_demux );
        p_access->psz_demux = strdup( psz_demux );
    }

614
    return p_block;
615

616 617 618 619 620
trash:
    block_Release( p_block );
    return NULL;
}

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 652 653 654
/*****************************************************************************
 * 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;
}

655 656 657 658 659
/*****************************************************************************
 * 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.
 ****************************************************************************/
660 661 662
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
663
    mtime_t   i_first = mdate();
664 665 666
    int       i_count = 0;
    block_t   *p = p_block;

667 668 669
    if( BlockParseRTP( p_access, p_block ) == NULL )
        return NULL;

670
    for( ;; )
671
    {
Marian Durkovic's avatar
Marian Durkovic committed
672
        mtime_t i_date = mdate();
673 674 675 676

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

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

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

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

698
static block_t *BlockStartRTP( access_t *p_access )
699
{
700
    p_access->pf_block = BlockRTP;
701
    return BlockPrebufferRTP( p_access, BlockUDP( p_access ) );
702 703
}

704

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

714 715
    if( ( p_block = BlockUDP( p_access ) ) == NULL )
        return NULL;
716

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

725 726 727
    if( p_block->i_buffer < RTP_HEADER_LEN )
        return p_block;

728
    /* Parse the header and make some verifications.
729
     * See RFC 3550. */
730

731
    i_rtp_version  = p_block->p_buffer[0] >> 6;
732
    i_payload_type = ( p_block->p_buffer[1] & 0x7F );
733

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

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

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

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

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

769
    p_access->pf_block = BlockRTP;
770
    return BlockPrebufferRTP( p_access, p_block );
771
}