rtp.c 16.7 KB
Newer Older
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1 2 3 4 5
/**
 * @file rtp.c
 * @brief Real-Time Protocol (RTP) demux module for VLC media player
 */
/*****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
6
 * Copyright (C) 2001-2005 VLC authors and VideoLAN
7
 * Copyright © 2007-2009 Rémi Denis-Courmont
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
8 9
 *
 * This library is free software; you can redistribute it and/or
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
12 13 14 15
 * of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
18
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19
 * You should have received a copy of the GNU Lesser General Public
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdarg.h>
#include <assert.h>

#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_network.h>
#include <vlc_plugin.h>
34
#include <vlc_dtls.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
35 36

#include "rtp.h"
37
#ifdef HAVE_SRTP
38
# include "srtp.h"
39 40
# include <gcrypt.h>
# include <vlc_gcrypt.h>
41
#endif
42
#include "sdp.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
43 44 45 46 47 48

/*
 * TODO: so much stuff
 * - send RTCP-RR and RTCP-BYE
 * - dynamic payload types (need SDP parser)
 * - multiple medias (need SDP parser, and RTCP-SR parser for lip-sync)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
49
 * - support for stream_filter in case of chained demux (MPEG-TS)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
50 51
 */

52 53 54 55 56 57 58 59
#ifndef IPPROTO_DCCP
# define IPPROTO_DCCP 33 /* IANA */
#endif

#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136 /* from IANA */
#endif

60 61 62 63 64 65 66 67 68 69
/**
 * Extracts port number from "[host]:port" or "host:port" strings,
 * and remove brackets from the host name.
 * @param phost pointer to the string upon entry,
 * pointer to the hostname upon return.
 * @return port number, 0 if missing.
 */
static int extract_port (char **phost)
{
    char *host = *phost, *port;
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    if (host[0] == '[')
    {
        host = ++*phost; /* skip '[' */
        port = strchr (host, ']');
        if (port)
            *port++ = '\0'; /* skip ']' */
    }
    else
        port = strchr (host, ':');

    if (port == NULL)
        return 0;
    *port++ = '\0'; /* skip ':' */
    return atoi (port);
}

/**
 * Control callback
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
89
 */
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
static int Control (demux_t *demux, int query, va_list args)
{
    demux_sys_t *sys = demux->p_sys;

    switch (query)
    {
        case DEMUX_GET_PTS_DELAY:
        {
            *va_arg (args, vlc_tick_t *) =
                VLC_TICK_FROM_MS( var_InheritInteger (demux, "network-caching") );
            return VLC_SUCCESS;
        }

        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_SEEK:
        case DEMUX_CAN_CONTROL_PACE:
        {
            bool *v = va_arg( args, bool * );
            *v = false;
            return VLC_SUCCESS;
        }
    }

    if (sys->chained_demux != NULL)
        return vlc_demux_chained_ControlVa (sys->chained_demux, query, args);

    switch (query)
    {
        case DEMUX_GET_POSITION:
        {
            float *v = va_arg (args, float *);
            *v = 0.;
            return VLC_SUCCESS;
        }

        case DEMUX_GET_LENGTH:
        case DEMUX_GET_TIME:
        {
            *va_arg (args, vlc_tick_t *) = 0;
            return VLC_SUCCESS;
        }
    }

    return VLC_EGENERIC;
}

/**
 * Releases resources
 */
static void Close (vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;
    demux_sys_t *p_sys = demux->p_sys;

144 145
    vlc_cancel(p_sys->thread);
    vlc_join(p_sys->thread, NULL);
146 147 148 149
#ifdef HAVE_SRTP
    if (p_sys->srtp)
        srtp_destroy (p_sys->srtp);
#endif
150 151 152 153
    rtp_session_destroy (demux, p_sys->session);
    if (p_sys->rtcp_sock != NULL)
        vlc_dtls_Close(p_sys->rtcp_sock);
    vlc_dtls_Close(p_sys->rtp_sock);
154
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
155

156 157 158 159 160 161 162 163 164 165 166 167 168 169
static int OpenSDP(vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;
    uint64_t size;
    const unsigned char *peek;

    assert(demux->out != NULL);

    if (vlc_stream_Peek(demux->s, &peek, 3) < 3 || memcmp(peek, "v=0", 3))
        return VLC_EGENERIC; /* not an SDP */

    if (vlc_stream_GetSize(demux->s, &size))
        size = 65536;
    else if (size > 65536) {
170
        msg_Err(obj, "SDP description too large: %" PRIu64 " bytes", size);
171 172 173 174 175 176 177 178
        return VLC_EGENERIC;
    }

    /* We must peek so that fallback to another plugin works. */
    ssize_t sdplen = vlc_stream_Peek(demux->s, &peek, size);
    if (sdplen < 0)
        return sdplen;

179 180 181 182
    demux_sys_t *sys = vlc_obj_malloc(obj, sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

183 184
    sys->rtp_sock = NULL;
    sys->rtcp_sock = NULL;
185 186 187 188 189
    sys->session = NULL;
#ifdef HAVE_SRTP
    sys->srtp = NULL;
#endif

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    struct vlc_sdp *sdp = vlc_sdp_parse((const char *)peek, sdplen);
    if (sdp == NULL) {
        msg_Err(obj, "SDP description parse error");
        return VLC_EGENERIC;
    }

    struct vlc_sdp_media *media = sdp->media;
    if (media == NULL || media->next != NULL) {
        msg_Dbg(obj, "only one SDP m= line supported");
        goto error;
    }

    /* Check payload type (FIXME: handle multiple, match w/ a=rtpmap) */
    unsigned char pt = atoi(media->format);
    if (pt >= 64 || !(UINT64_C(0x300005d09) & (UINT64_C(1) << pt))) {
        msg_Dbg(obj, "unsupported payload format(s) %s", media->format);
        goto error;
    }

    if (vlc_sdp_media_attr_value(media, "control") != NULL
     || vlc_sdp_attr_value(sdp, "control") != NULL) {
        msg_Dbg(obj, "RTSP not supported");
        goto error;
    }

    struct vlc_sdp_conn *conn = media->conns;
    if (conn != NULL && conn->next != NULL) {
        msg_Dbg(obj, "only one SDP c= line supported");
        goto error;
    }

    if (conn == NULL)
        conn = sdp->conn;
    if (conn == NULL) {
        msg_Err(obj, "missing SDP c= line");
        goto error;
    }

    /* Determine destination port numbers */
    unsigned int rtp_port, rtcp_port;

    if (!vlc_sdp_media_attr_present(media, "rtcp-mux")) {
        const char *rtcp = vlc_sdp_media_attr_value(media, "rtcp");

        if (rtcp != NULL) {
            /* Explicit RTCP port */
            char *end;
            unsigned long x = strtoul(rtcp, &end, 10);

            if (*end || x == 0 || x > 65535) {
                msg_Err(obj, "invalid RTCP port specification %s", rtcp);
                goto error;
            }

            rtp_port = media->port;
            rtcp_port = x;
        } else {
            /* Implicit RTCP port (next odd) */
            rtp_port = (media->port + 1) & ~1;
            rtcp_port = media->port | 1;
        }
    } else {
        /* RTCP muxed on same port RTP */
        rtp_port = media->port;
        rtcp_port = 0;
    }

    /* TODO: support other protocols */
    if (strcmp(media->proto, "RTP/AVP") != 0) {
        msg_Dbg(obj, "unsupported protocol %s", media->proto);
        goto error;
    }

    /* Determine source address */
    char srcbuf[256], *src = NULL;
    const char *sfilter = vlc_sdp_media_attr_value(media, "source-filter");
    if (sfilter == NULL)
        sfilter = vlc_sdp_attr_value(sdp, "source-filter");
    /* FIXME: handle multiple source-filter attributes, match destination,
     * check IP version */
    if (sfilter != NULL
     && sscanf(sfilter, " incl IN IP%*1[46] %*s %255s", srcbuf) == 1)
        src = srcbuf;

    /* FIXME: enforce address family */
    int fd = net_OpenDgram(obj, conn->addr, rtp_port, src, 0, IPPROTO_UDP);
    if (fd == -1)
        goto error;

279 280 281 282 283 284
    sys->rtp_sock = vlc_datagram_CreateFD(fd);
    if (unlikely(sys->rtp_sock == NULL)) {
        net_Close(fd);
        goto error;
    }

285
    if (rtcp_port > 0) {
286 287 288 289 290 291 292
        fd = net_OpenDgram(obj, conn->addr, rtcp_port, src, 0, IPPROTO_UDP);
        if (fd == -1)
            goto error;

        sys->rtcp_sock = vlc_datagram_CreateFD(fd);
        if (unlikely(sys->rtcp_sock == NULL)) {
            net_Close(fd);
293
            goto error;
294
        }
295 296 297
    }

    vlc_sdp_free(sdp);
298
    sdp = NULL;
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

    sys->chained_demux = NULL;
    sys->max_src = var_InheritInteger(obj, "rtp-max-src");
    sys->timeout = vlc_tick_from_sec(var_InheritInteger(obj, "rtp-timeout"));
    sys->max_dropout  = var_InheritInteger(obj, "rtp-max-dropout");
    sys->max_misorder = var_InheritInteger(obj, "rtp-max-misorder");
    sys->autodetect = true;

    demux->pf_demux = NULL;
    demux->pf_control = Control;
    demux->p_sys = sys;

    sys->session = rtp_session_create(demux);
    if (sys->session == NULL)
        goto error;

    if (vlc_clone(&sys->thread, rtp_dgram_thread, demux,
316 317
                  VLC_THREAD_PRIORITY_INPUT)) {
        rtp_session_destroy(demux, sys->session);
318
        goto error;
319
    }
320 321 322
    return VLC_SUCCESS;

error:
323 324 325 326 327 328
    if (sys->rtcp_sock != NULL)
        vlc_dtls_Close(sys->rtcp_sock);
    if (sys->rtp_sock != NULL)
        vlc_dtls_Close(sys->rtp_sock);
    if (sdp != NULL)
        vlc_sdp_free(sdp);
329 330 331
    return VLC_EGENERIC;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
332 333 334
/**
 * Probes and initializes.
 */
335
static int OpenURL(vlc_object_t *obj)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
336 337
{
    demux_t *demux = (demux_t *)obj;
338 339
    int tp; /* transport protocol */

340 341 342
    if (demux->out == NULL)
        return VLC_EGENERIC;

343
    if (!strcasecmp(demux->psz_name, "dccp"))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344 345
        tp = IPPROTO_DCCP;
    else
346
    if (!strcasecmp(demux->psz_name, "rtp"))
347 348
        tp = IPPROTO_UDP;
    else
349
    if (!strcasecmp(demux->psz_name, "udplite"))
350 351
        tp = IPPROTO_UDPLITE;
    else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
352 353
        return VLC_EGENERIC;

354 355 356 357
    demux_sys_t *p_sys = vlc_obj_malloc(obj, sizeof (*p_sys));
    if (unlikely(p_sys == NULL))
        return VLC_ENOMEM;

358
    char *tmp = strdup (demux->psz_location);
359
    if (tmp == NULL)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
360 361
        return VLC_ENOMEM;

362 363 364 365 366 367 368 369 370 371 372 373
    char *shost;
    char *dhost = strchr (tmp, '@');
    if (dhost != NULL)
    {
        *(dhost++) = '\0';
        shost = tmp;
    }
    else
    {
        dhost = tmp;
        shost = NULL;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
374 375 376

    /* Parses the port numbers */
    int sport = 0, dport = 0;
377 378
    if (shost != NULL)
        sport = extract_port (&shost);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
379 380 381 382 383
    if (dhost != NULL)
        dport = extract_port (&dhost);
    if (dport == 0)
        dport = 5004; /* avt-profile-1 port */

384 385
    int rtcp_dport = var_CreateGetInteger (obj, "rtcp-port");

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
386
    /* Try to connect */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
387
    int fd = -1, rtcp_fd = -1;
388
    bool co = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
389 390 391 392 393

    switch (tp)
    {
        case IPPROTO_UDP:
        case IPPROTO_UDPLITE:
394
            fd = net_OpenDgram (obj, dhost, dport, shost, sport, tp);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
395 396
            if (fd == -1)
                break;
397
            if (rtcp_dport > 0) /* XXX: source port is unknown */
398
                rtcp_fd = net_OpenDgram (obj, dhost, rtcp_dport, shost, 0, tp);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
399 400 401 402 403 404 405 406 407 408
            break;

         case IPPROTO_DCCP:
#ifndef SOCK_DCCP /* provisional API (FIXME) */
# ifdef __linux__
#  define SOCK_DCCP 6
# endif
#endif
#ifdef SOCK_DCCP
            var_Create (obj, "dccp-service", VLC_VAR_STRING);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
409
            var_SetString (obj, "dccp-service", "RTPV"); /* FIXME: RTPA? */
410
            fd = net_Connect (obj, dhost, dport, SOCK_DCCP, tp);
411
            co = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
412 413 414 415 416 417
#else
            msg_Err (obj, "DCCP support not included");
#endif
            break;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
418
    free (tmp);
419 420
    p_sys->rtp_sock = (co ? vlc_dccp_CreateFD : vlc_datagram_CreateFD)(fd);
    if (p_sys->rtcp_sock == NULL) {
421 422
        if (rtcp_fd != -1)
            net_Close(rtcp_fd);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
423
        return VLC_EGENERIC;
424
    }
425
    net_SetCSCov (fd, -1, 12);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
426

427 428 429 430 431 432 433
    if (rtcp_fd != -1) {
        p_sys->rtcp_sock = vlc_datagram_CreateFD(rtcp_fd);
        if (p_sys->rtcp_sock == NULL)
            net_Close (rtcp_fd);
    } else
        p_sys->rtcp_sock = NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
434
    /* Initializes demux */
435
    p_sys->chained_demux = NULL;
436
#ifdef HAVE_SRTP
437
    p_sys->srtp         = NULL;
438
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
439
    p_sys->max_src      = var_CreateGetInteger (obj, "rtp-max-src");
440
    p_sys->timeout      = vlc_tick_from_sec( var_CreateGetInteger (obj, "rtp-timeout") );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
441 442
    p_sys->max_dropout  = var_CreateGetInteger (obj, "rtp-max-dropout");
    p_sys->max_misorder = var_CreateGetInteger (obj, "rtp-max-misorder");
443
    p_sys->autodetect   = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
444

445
    demux->pf_demux   = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
446 447 448 449 450 451 452
    demux->pf_control = Control;
    demux->p_sys      = p_sys;

    p_sys->session = rtp_session_create (demux);
    if (p_sys->session == NULL)
        goto error;

453
#ifdef HAVE_SRTP
454
    char *key = var_CreateGetNonEmptyString (demux, "srtp-key");
455 456
    if (key)
    {
457
        vlc_gcrypt_init ();
458
        p_sys->srtp = srtp_create (SRTP_ENCR_AES_CM, SRTP_AUTH_HMAC_SHA1, 10,
459
                                   SRTP_PRF_AES_CM, SRTP_RCC_MODE1);
460 461 462 463 464 465
        if (p_sys->srtp == NULL)
        {
            free (key);
            goto error;
        }

466
        char *salt = var_CreateGetNonEmptyString (demux, "srtp-salt");
467
        int val = srtp_setkeystring (p_sys->srtp, key, salt ? salt : "");
468 469
        free (salt);
        free (key);
470
        if (val)
471
        {
472 473
            msg_Err (obj, "bad SRTP key/salt combination (%s)",
                     vlc_strerror_c(val));
474 475 476
            goto error;
        }
    }
477
#endif
478

479
    if (vlc_clone (&p_sys->thread, rtp_dgram_thread,
480
                   demux, VLC_THREAD_PRIORITY_INPUT))
481
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
482 483 484
    return VLC_SUCCESS;

error:
485 486 487 488 489 490 491
    if (p_sys->srtp != NULL)
        srtp_destroy(p_sys->srtp);
    if (p_sys->session != NULL)
        rtp_session_destroy(demux, p_sys->session);
    if (p_sys->rtcp_sock != NULL)
        vlc_dtls_Close(p_sys->rtcp_sock);
    vlc_dtls_Close(p_sys->rtp_sock);
492
    return VLC_EGENERIC;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
493 494
}

495 496 497 498
#define RTCP_PORT_TEXT N_("RTCP (local) port")
#define RTCP_PORT_LONGTEXT N_( \
    "RTCP packets will be received on this transport protocol port. " \
    "If zero, multiplexed RTP/RTCP is used.")
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
499

500 501 502 503 504
#define SRTP_KEY_TEXT N_("SRTP key (hexadecimal)")
#define SRTP_KEY_LONGTEXT N_( \
    "RTP packets will be authenticated and deciphered "\
    "with this Secure RTP master shared secret key. "\
    "This must be a 32-character-long hexadecimal string.")
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
505

506 507 508 509
#define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)")
#define SRTP_SALT_LONGTEXT N_( \
    "Secure RTP requires a (non-secret) master salt value. " \
    "This must be a 28-character-long hexadecimal string.")
510

511 512 513
#define RTP_MAX_SRC_TEXT N_("Maximum RTP sources")
#define RTP_MAX_SRC_LONGTEXT N_( \
    "How many distinct active RTP sources are allowed at a time." )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
514

515 516 517
#define RTP_TIMEOUT_TEXT N_("RTP source timeout (sec)")
#define RTP_TIMEOUT_LONGTEXT N_( \
    "How long to wait for any packet before a source is expired.")
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
518

519 520 521 522
#define RTP_MAX_DROPOUT_TEXT N_("Maximum RTP sequence number dropout")
#define RTP_MAX_DROPOUT_LONGTEXT N_( \
    "RTP packets will be discarded if they are too much ahead (i.e. in the " \
    "future) by this many packets from the last received packet." )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
523

524 525 526 527
#define RTP_MAX_MISORDER_TEXT N_("Maximum RTP sequence number misordering")
#define RTP_MAX_MISORDER_LONGTEXT N_( \
    "RTP packets will be discarded if they are too far behind (i.e. in the " \
    "past) by this many packets from the last received packet." )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
528

529 530 531 532 533 534
#define RTP_DYNAMIC_PT_TEXT N_("RTP payload format assumed for dynamic " \
                               "payloads")
#define RTP_DYNAMIC_PT_LONGTEXT N_( \
    "This payload format will be assumed for dynamic payload types " \
    "(between 96 and 127) if it can't be determined otherwise with " \
    "out-of-band mappings (SDP)" )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
535

536 537
static const char *const dynamic_pt_list[] = { "theora" };
static const char *const dynamic_pt_list_text[] = { "Theora Encoded Video" };
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
538

539 540
/*
 * Module descriptor
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
541
 */
542 543 544 545 546
vlc_module_begin()
    set_shortname(N_("RTP"))
    set_description(N_("Real-Time Protocol (RTP) input"))
    set_category(CAT_INPUT)
    set_subcategory(SUBCAT_INPUT_DEMUX)
547 548 549 550
    set_capability("demux", 55)
    set_callbacks(OpenSDP, Close)

    add_submodule()
551
    set_capability("access", 0)
552
    set_callbacks(OpenURL, Close)
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579

    add_integer("rtcp-port", 0, RTCP_PORT_TEXT,
                 RTCP_PORT_LONGTEXT, false)
        change_integer_range(0, 65535)
        change_safe()
#ifdef HAVE_SRTP
    add_string ("srtp-key", "",
                SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false)
        change_safe ()
    add_string("srtp-salt", "",
               SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false)
        change_safe()
#endif
    add_integer("rtp-max-src", 1, RTP_MAX_SRC_TEXT,
                RTP_MAX_SRC_LONGTEXT, true)
        change_integer_range (1, 255)
    add_integer("rtp-timeout", 5, RTP_TIMEOUT_TEXT,
                RTP_TIMEOUT_LONGTEXT, true)
    add_integer("rtp-max-dropout", 3000, RTP_MAX_DROPOUT_TEXT,
                RTP_MAX_DROPOUT_LONGTEXT, true)
        change_integer_range (0, 32767)
    add_integer("rtp-max-misorder", 100, RTP_MAX_MISORDER_TEXT,
                RTP_MAX_MISORDER_LONGTEXT, true)
        change_integer_range (0, 32767)
    add_string("rtp-dynamic-pt", NULL, RTP_DYNAMIC_PT_TEXT,
               RTP_DYNAMIC_PT_LONGTEXT, true)
        change_string_list(dynamic_pt_list, dynamic_pt_list_text)
580

581
    /*add_shortcut ("sctp")*/
582
    add_shortcut("dccp", "rtp", "udplite")
583
vlc_module_end()