You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
701 lines
20 KiB
701 lines
20 KiB
/**
|
|
* @file rtp.c
|
|
* @brief Real-Time Protocol (RTP) demux module for VLC media player
|
|
*/
|
|
/*****************************************************************************
|
|
* Copyright (C) 2001-2005 VLC authors and VideoLAN
|
|
* Copyright © 2007-2009 Rémi Denis-Courmont
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* 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
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* 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>
|
|
#include "vlc_dtls.h"
|
|
#include <vlc_modules.h> /* module_exists() */
|
|
|
|
#include "rtp.h"
|
|
#ifdef HAVE_SRTP
|
|
# include "srtp.h"
|
|
# include <gcrypt.h>
|
|
# include <vlc_gcrypt.h>
|
|
#endif
|
|
#include "sdp.h"
|
|
#include "input.h"
|
|
|
|
/*
|
|
* TODO: so much stuff
|
|
* - send RTCP-RR and RTCP-BYE
|
|
* - multiple medias (need SDP parser, and RTCP-SR parser for lip-sync)
|
|
* - support for stream_filter in case of chained demux (MPEG-TS)
|
|
*/
|
|
|
|
#ifndef IPPROTO_DCCP
|
|
# define IPPROTO_DCCP 33 /* IANA */
|
|
#endif
|
|
|
|
#ifndef IPPROTO_UDPLITE
|
|
# define IPPROTO_UDPLITE 136 /* from IANA */
|
|
#endif
|
|
|
|
struct vlc_rtp_es_id {
|
|
struct vlc_rtp_es es;
|
|
es_out_t *out;
|
|
es_out_id_t *id;
|
|
};
|
|
|
|
static void vlc_rtp_es_id_destroy(struct vlc_rtp_es *es)
|
|
{
|
|
struct vlc_rtp_es_id *ei = container_of(es, struct vlc_rtp_es_id, es);
|
|
|
|
es_out_Del(ei->out, ei->id);
|
|
free(ei);
|
|
}
|
|
|
|
static void vlc_rtp_es_id_send(struct vlc_rtp_es *es, block_t *block)
|
|
{
|
|
struct vlc_rtp_es_id *ei = container_of(es, struct vlc_rtp_es_id, es);
|
|
|
|
/* TODO: Don't set PCR here. Breaks multiple sources (in a session)
|
|
* and more importantly eventually multiple sessions. */
|
|
vlc_tick_t pcr = (block->i_dts != VLC_TICK_INVALID) ? block->i_dts
|
|
: block->i_pts;
|
|
if (pcr != VLC_TICK_INVALID)
|
|
es_out_SetPCR(ei->out, pcr);
|
|
es_out_Send(ei->out, ei->id, block);
|
|
}
|
|
|
|
static const struct vlc_rtp_es_operations vlc_rtp_es_id_ops = {
|
|
vlc_rtp_es_id_destroy, vlc_rtp_es_id_send,
|
|
};
|
|
|
|
static struct vlc_rtp_es *vlc_rtp_es_request(struct vlc_rtp_pt *pt,
|
|
const es_format_t *restrict fmt)
|
|
{
|
|
demux_t *demux = pt->owner.data;
|
|
|
|
struct vlc_rtp_es_id *ei = malloc(sizeof (*ei));
|
|
if (unlikely(ei == NULL))
|
|
return vlc_rtp_es_dummy;
|
|
|
|
ei->es.ops = &vlc_rtp_es_id_ops;
|
|
ei->out = demux->out;
|
|
ei->id = es_out_Add(demux->out, fmt);
|
|
if (ei->id == NULL) {
|
|
free(ei);
|
|
return vlc_rtp_es_dummy;
|
|
}
|
|
return &ei->es;
|
|
}
|
|
|
|
struct vlc_rtp_es_mux {
|
|
struct vlc_rtp_es es;
|
|
vlc_demux_chained_t *chained_demux;
|
|
};
|
|
|
|
static void vlc_rtp_es_mux_destroy(struct vlc_rtp_es *es)
|
|
{
|
|
struct vlc_rtp_es_mux *em = container_of(es, struct vlc_rtp_es_mux, es);
|
|
|
|
vlc_demux_chained_Delete(em->chained_demux);
|
|
free(em);
|
|
}
|
|
|
|
static void vlc_rtp_es_mux_send(struct vlc_rtp_es *es, block_t *block)
|
|
{
|
|
struct vlc_rtp_es_mux *em = container_of(es, struct vlc_rtp_es_mux, es);
|
|
|
|
vlc_demux_chained_Send(em->chained_demux, block);
|
|
}
|
|
|
|
static const struct vlc_rtp_es_operations vlc_rtp_es_mux_ops = {
|
|
vlc_rtp_es_mux_destroy, vlc_rtp_es_mux_send,
|
|
};
|
|
|
|
static struct vlc_rtp_es *vlc_rtp_mux_request(struct vlc_rtp_pt *pt,
|
|
const char *name)
|
|
{
|
|
demux_t *demux = pt->owner.data;
|
|
|
|
struct vlc_rtp_es_mux *em = malloc(sizeof (*em));
|
|
if (unlikely(em == NULL))
|
|
return vlc_rtp_es_dummy;
|
|
|
|
em->es.ops = &vlc_rtp_es_mux_ops;
|
|
em->chained_demux = vlc_demux_chained_New(VLC_OBJECT(demux), name,
|
|
demux->out);
|
|
if (em->chained_demux == NULL) {
|
|
free(em);
|
|
return NULL;
|
|
}
|
|
return &em->es;
|
|
}
|
|
|
|
static const struct vlc_rtp_pt_owner_operations vlc_rtp_pt_owner_ops = {
|
|
vlc_rtp_es_request, vlc_rtp_mux_request,
|
|
};
|
|
|
|
int vlc_rtp_pt_instantiate(vlc_object_t *obj, struct vlc_rtp_pt *restrict pt,
|
|
const struct vlc_sdp_pt *restrict desc)
|
|
{
|
|
char modname[32];
|
|
int ret = VLC_ENOTSUP;
|
|
|
|
if (strchr(desc->name, ',') != NULL)
|
|
/* Comma has special meaning in vlc_module_match(), forbid it */
|
|
return VLC_EINVAL;
|
|
if ((size_t)snprintf(modname, sizeof (modname), "%s/%s",
|
|
desc->media->type, desc->name) >= sizeof (modname))
|
|
return VLC_ENOTSUP; /* Outlandish media type with long name */
|
|
|
|
module_t **mods;
|
|
ssize_t n = vlc_module_match("rtp parser", modname, true, &mods, NULL);
|
|
|
|
for (ssize_t i = 0; i < n; i++) {
|
|
vlc_rtp_parser_cb cb = vlc_module_map(vlc_object_logger(obj), mods[i]);
|
|
if (cb == NULL)
|
|
continue;
|
|
|
|
ret = cb(obj, pt, desc);
|
|
if (ret == VLC_SUCCESS) {
|
|
msg_Dbg(obj, "- module \"%s\"", module_get_name(mods[i], true));
|
|
assert(pt->ops != NULL);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(mods);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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
|
|
*/
|
|
static int Control (demux_t *demux, int query, va_list args)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
rtp_sys_t *p_sys = demux->p_sys;
|
|
|
|
vlc_cancel(p_sys->thread);
|
|
vlc_join(p_sys->thread, NULL);
|
|
#ifdef HAVE_SRTP
|
|
if (p_sys->input_sys.srtp)
|
|
srtp_destroy (p_sys->input_sys.srtp);
|
|
#endif
|
|
rtp_session_destroy (obj->logger, p_sys->session);
|
|
if (p_sys->input_sys.rtcp_sock != NULL)
|
|
vlc_dtls_Close(p_sys->input_sys.rtcp_sock);
|
|
vlc_dtls_Close(p_sys->input_sys.rtp_sock);
|
|
}
|
|
|
|
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) {
|
|
msg_Err(obj, "SDP description too large: %" PRIu64 " bytes", size);
|
|
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;
|
|
|
|
rtp_sys_t *sys = vlc_obj_malloc(obj, sizeof (*sys));
|
|
if (unlikely(sys == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
sys->input_sys.rtp_sock = NULL;
|
|
sys->input_sys.rtcp_sock = NULL;
|
|
sys->session = NULL;
|
|
#ifdef HAVE_SRTP
|
|
sys->input_sys.srtp = NULL;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
sys->input_sys.rtp_sock = vlc_datagram_CreateFD(fd);
|
|
if (unlikely(sys->input_sys.rtp_sock == NULL)) {
|
|
net_Close(fd);
|
|
goto error;
|
|
}
|
|
|
|
if (rtcp_port > 0) {
|
|
fd = net_OpenDgram(obj, conn->addr, rtcp_port, src, 0, IPPROTO_UDP);
|
|
if (fd == -1)
|
|
goto error;
|
|
|
|
sys->input_sys.rtcp_sock = vlc_datagram_CreateFD(fd);
|
|
if (unlikely(sys->input_sys.rtcp_sock == NULL)) {
|
|
net_Close(fd);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
sys->logger = obj->logger;
|
|
|
|
demux->pf_demux = NULL;
|
|
demux->pf_control = Control;
|
|
demux->p_sys = sys;
|
|
|
|
sys->session = rtp_session_create_custom(var_InheritInteger(obj, "rtp-max-dropout"),
|
|
var_InheritInteger(obj, "rtp-max-misorder"),
|
|
var_InheritInteger(obj, "rtp-max-src"),
|
|
vlc_tick_from_sec(var_InheritInteger(obj, "rtp-timeout")));
|
|
if (sys->session == NULL)
|
|
goto error;
|
|
|
|
/* Parse payload types */
|
|
const struct vlc_rtp_pt_owner pt_owner = { &vlc_rtp_pt_owner_ops, demux };
|
|
int err = vlc_rtp_add_media_types(obj, sys->session, media, &pt_owner);
|
|
if (err < 0) {
|
|
msg_Err(obj, "SDP description parse error");
|
|
goto error;
|
|
}
|
|
if (err > 0 && module_exists("live555")) /* Bail out to live555 */
|
|
goto error;
|
|
|
|
if (vlc_clone(&sys->thread, rtp_dgram_thread, sys)) {
|
|
rtp_session_destroy(obj->logger, sys->session);
|
|
goto error;
|
|
}
|
|
|
|
vlc_sdp_free(sdp);
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
if (sys->input_sys.rtcp_sock != NULL)
|
|
vlc_dtls_Close(sys->input_sys.rtcp_sock);
|
|
if (sys->input_sys.rtp_sock != NULL)
|
|
vlc_dtls_Close(sys->input_sys.rtp_sock);
|
|
vlc_sdp_free(sdp);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/**
|
|
* Probes and initializes.
|
|
*/
|
|
static int OpenURL(vlc_object_t *obj)
|
|
{
|
|
demux_t *demux = (demux_t *)obj;
|
|
int tp; /* transport protocol */
|
|
|
|
if (demux->out == NULL)
|
|
return VLC_EGENERIC;
|
|
|
|
if (!strcasecmp(demux->psz_name, "dccp"))
|
|
tp = IPPROTO_DCCP;
|
|
else
|
|
if (!strcasecmp(demux->psz_name, "rtp"))
|
|
tp = IPPROTO_UDP;
|
|
else
|
|
if (!strcasecmp(demux->psz_name, "udplite"))
|
|
tp = IPPROTO_UDPLITE;
|
|
else
|
|
return VLC_EGENERIC;
|
|
|
|
rtp_sys_t *p_sys = vlc_obj_malloc(obj, sizeof (*p_sys));
|
|
if (unlikely(p_sys == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
char *tmp = strdup (demux->psz_location);
|
|
if (tmp == NULL)
|
|
return VLC_ENOMEM;
|
|
|
|
char *shost;
|
|
char *dhost = strchr (tmp, '@');
|
|
if (dhost != NULL)
|
|
{
|
|
*(dhost++) = '\0';
|
|
shost = tmp;
|
|
}
|
|
else
|
|
{
|
|
dhost = tmp;
|
|
shost = NULL;
|
|
}
|
|
|
|
/* Parses the port numbers */
|
|
int sport = 0, dport = 0;
|
|
if (shost != NULL)
|
|
sport = extract_port (&shost);
|
|
if (dhost != NULL)
|
|
dport = extract_port (&dhost);
|
|
if (dport == 0)
|
|
dport = 5004; /* avt-profile-1 port */
|
|
|
|
int rtcp_dport = var_CreateGetInteger (obj, "rtcp-port");
|
|
|
|
/* Try to connect */
|
|
int fd = -1, rtcp_fd = -1;
|
|
bool co = false;
|
|
|
|
switch (tp)
|
|
{
|
|
case IPPROTO_UDP:
|
|
case IPPROTO_UDPLITE:
|
|
fd = net_OpenDgram (obj, dhost, dport, shost, sport, tp);
|
|
if (fd == -1)
|
|
break;
|
|
if (rtcp_dport > 0) /* XXX: source port is unknown */
|
|
rtcp_fd = net_OpenDgram (obj, dhost, rtcp_dport, shost, 0, tp);
|
|
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);
|
|
var_SetString (obj, "dccp-service", "RTPV"); /* FIXME: RTPA? */
|
|
fd = net_Connect (obj, dhost, dport, SOCK_DCCP, tp);
|
|
co = true;
|
|
#else
|
|
msg_Err (obj, "DCCP support not included");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
free (tmp);
|
|
|
|
if(fd == -1)
|
|
return VLC_EGENERIC;
|
|
|
|
p_sys->input_sys.rtp_sock = (co ? vlc_dccp_CreateFD : vlc_datagram_CreateFD)(fd);
|
|
if (p_sys->input_sys.rtp_sock == NULL) {
|
|
if (rtcp_fd != -1)
|
|
net_Close(rtcp_fd);
|
|
return VLC_EGENERIC;
|
|
}
|
|
net_SetCSCov (fd, -1, 12);
|
|
|
|
if (rtcp_fd != -1) {
|
|
p_sys->input_sys.rtcp_sock = vlc_datagram_CreateFD(rtcp_fd);
|
|
if (p_sys->input_sys.rtcp_sock == NULL)
|
|
net_Close (rtcp_fd);
|
|
} else
|
|
p_sys->input_sys.rtcp_sock = NULL;
|
|
|
|
#ifdef HAVE_SRTP
|
|
p_sys->input_sys.srtp = NULL;
|
|
#endif
|
|
p_sys->logger = obj->logger;
|
|
|
|
demux->pf_demux = NULL;
|
|
demux->pf_control = Control;
|
|
demux->p_sys = p_sys;
|
|
|
|
p_sys->session = rtp_session_create_custom(
|
|
var_InheritInteger(obj, "rtp-max-dropout"),
|
|
var_InheritInteger(obj, "rtp-max-misorder"),
|
|
var_InheritInteger(obj, "rtp-max-src"),
|
|
vlc_tick_from_sec(var_InheritInteger(obj, "rtp-timeout")) );
|
|
if (p_sys->session == NULL)
|
|
goto error;
|
|
|
|
const struct vlc_rtp_pt_owner pt_owner = { &vlc_rtp_pt_owner_ops, demux };
|
|
rtp_autodetect(VLC_OBJECT(demux), p_sys->session, &pt_owner);
|
|
|
|
#ifdef HAVE_SRTP
|
|
char *key = var_CreateGetNonEmptyString (demux, "srtp-key");
|
|
if (key)
|
|
{
|
|
vlc_gcrypt_init ();
|
|
p_sys->input_sys.srtp = srtp_create (SRTP_ENCR_AES_CM, SRTP_AUTH_HMAC_SHA1, 10,
|
|
SRTP_PRF_AES_CM, SRTP_RCC_MODE1);
|
|
if (p_sys->input_sys.srtp == NULL)
|
|
{
|
|
free (key);
|
|
goto error;
|
|
}
|
|
|
|
char *salt = var_CreateGetNonEmptyString (demux, "srtp-salt");
|
|
int val = srtp_setkeystring (p_sys->input_sys.srtp, key, salt ? salt : "");
|
|
free (salt);
|
|
free (key);
|
|
if (val)
|
|
{
|
|
msg_Err (obj, "bad SRTP key/salt combination (%s)",
|
|
vlc_strerror_c(val));
|
|
goto error;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (vlc_clone (&p_sys->thread, rtp_dgram_thread, p_sys))
|
|
goto error;
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
#ifdef HAVE_SRTP
|
|
if (p_sys->input_sys.srtp != NULL)
|
|
srtp_destroy(p_sys->input_sys.srtp);
|
|
#endif
|
|
if (p_sys->session != NULL)
|
|
rtp_session_destroy(obj->logger, p_sys->session);
|
|
if (p_sys->input_sys.rtcp_sock != NULL)
|
|
vlc_dtls_Close(p_sys->input_sys.rtcp_sock);
|
|
vlc_dtls_Close(p_sys->input_sys.rtp_sock);
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
#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.")
|
|
|
|
#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.")
|
|
|
|
#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.")
|
|
|
|
#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." )
|
|
|
|
#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.")
|
|
|
|
#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." )
|
|
|
|
#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." )
|
|
|
|
/*
|
|
* Module descriptor
|
|
*/
|
|
vlc_module_begin()
|
|
set_shortname(N_("RTP"))
|
|
set_description(N_("Real-Time Protocol (RTP) input"))
|
|
set_subcategory(SUBCAT_INPUT_DEMUX)
|
|
set_capability("demux", 55)
|
|
set_callbacks(OpenSDP, Close)
|
|
|
|
add_submodule()
|
|
set_capability("access", 0)
|
|
set_callbacks(OpenURL, Close)
|
|
|
|
add_integer("rtcp-port", 0, RTCP_PORT_TEXT,
|
|
RTCP_PORT_LONGTEXT)
|
|
change_integer_range(0, 65535)
|
|
change_safe()
|
|
#ifdef HAVE_SRTP
|
|
add_string ("srtp-key", "",
|
|
SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT)
|
|
change_safe ()
|
|
add_string("srtp-salt", "",
|
|
SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT)
|
|
change_safe()
|
|
#endif
|
|
add_integer("rtp-max-src", RTP_MAX_SRC_DEFAULT, RTP_MAX_SRC_TEXT,
|
|
RTP_MAX_SRC_LONGTEXT)
|
|
change_integer_range (1, 255)
|
|
add_integer("rtp-timeout", RTP_MAX_TIMEOUT_DEFAULT, RTP_TIMEOUT_TEXT,
|
|
RTP_TIMEOUT_LONGTEXT)
|
|
add_integer("rtp-max-dropout", RTP_MAX_DROPOUT_DEFAULT, RTP_MAX_DROPOUT_TEXT,
|
|
RTP_MAX_DROPOUT_LONGTEXT)
|
|
change_integer_range (0, 32767)
|
|
add_integer("rtp-max-misorder", RTP_MAX_MISORDER_DEFAULT, RTP_MAX_MISORDER_TEXT,
|
|
RTP_MAX_MISORDER_LONGTEXT)
|
|
change_integer_range (0, 32767)
|
|
add_obsolete_string("rtp-dynamic-pt") /* since 4.0.0 */
|
|
|
|
/*add_shortcut ("sctp")*/
|
|
add_shortcut("dccp", "rtp", "udplite")
|
|
vlc_module_end()
|
|
|