X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Frtp.c;h=5978918c09aab433d6c3cc1c613c4805b240d1bc;hb=ebbd98f73d95b5f48d537401874bbb129c020c7e;hp=8641e897e58830b78fb7198463bdce76dcc080ea;hpb=7e42e3223bf8b5bb8baa8477b3636cec3dcbb72a;p=vlc diff --git a/modules/demux/rtp.c b/modules/demux/rtp.c index 8641e897e5..5978918c09 100644 --- a/modules/demux/rtp.c +++ b/modules/demux/rtp.c @@ -31,16 +31,29 @@ #include #include #include +#ifdef HAVE_POLL +# include +#endif #include #include #include "rtp.h" +#include #define RTP_CACHING_TEXT N_("RTP de-jitter buffer length (msec)") #define RTP_CACHING_LONGTEXT N_( \ "How long to wait for late RTP packets (and delay the performance)." ) +#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.") + +#define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)") +#define SRTP_SALT_LONGTEXT N_( \ + "Secure RTP requires a (non-secret) master salt value.") + #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." ) @@ -70,12 +83,16 @@ vlc_module_begin (); set_description (_("(Experimental) Real-Time Protocol demuxer")); set_category (CAT_INPUT); set_subcategory (SUBCAT_INPUT_DEMUX); - set_capability ("access_demux", 10); + set_capability ("access_demux", 0); set_callbacks (Open, Close); add_integer ("rtp-caching", 1000, NULL, RTP_CACHING_TEXT, RTP_CACHING_LONGTEXT, true); change_integer_range (0, 65535); + add_string ("srtp-key", "", NULL, + SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false); + add_string ("srtp-salt", "", NULL, + SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false); add_integer ("rtp-max-src", 1, NULL, RTP_MAX_SRC_TEXT, RTP_MAX_SRC_LONGTEXT, true); change_integer_range (1, 255); @@ -88,7 +105,11 @@ vlc_module_begin (); RTP_MAX_MISORDER_LONGTEXT, true); change_integer_range (0, 32767); + add_shortcut ("dccp"); + /*add_shortcut ("sctp");*/ + add_shortcut ("rtptcp"); /* "tcp" is already taken :( */ add_shortcut ("rtp"); + add_shortcut ("udplite"); vlc_module_end (); /* @@ -99,6 +120,15 @@ vlc_module_end (); * - support for access_filter in case of stream_Demux (MPEG-TS) */ +#ifndef IPPROTO_DCCP +# define IPPROTO_DCCP 33 /* IANA */ +#endif + +#ifndef IPPROTO_UDPLITE +# define IPPROTO_UDPLITE 136 /* from IANA */ +#endif + + /* * Local prototypes */ @@ -112,8 +142,20 @@ static int extract_port (char **phost); static int Open (vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; + int tp; /* transport protocol */ - if (strcmp (demux->psz_access, "rtp")) + if (!strcmp (demux->psz_access, "dccp")) + tp = IPPROTO_DCCP; + else + if (!strcmp (demux->psz_access, "rtptcp")) + tp = IPPROTO_TCP; + else + if (!strcmp (demux->psz_access, "rtp")) + tp = IPPROTO_UDP; + else + if (!strcmp (demux->psz_access, "udplite")) + tp = IPPROTO_UDPLITE; + else return VLC_EGENERIC; char *tmp = strdup (demux->psz_path); @@ -134,23 +176,65 @@ static int Open (vlc_object_t *obj) dport = 5004; /* avt-profile-1 port */ /* Try to connect */ - int fd = net_OpenDgram (obj, dhost, dport, shost, sport, - AF_UNSPEC, IPPROTO_UDP); + int fd = -1, rtcp_fd = -1; + + switch (tp) + { + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + fd = net_OpenDgram (obj, dhost, (dport + 1) & ~1, + shost, (sport + 1) & ~1, AF_UNSPEC, tp); + if (fd == -1) + break; + rtcp_fd = net_OpenDgram (obj, dhost, dport | 1, shost, + sport ? (sport | 1) : 0, AF_UNSPEC, 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, shost, (sport + 1) & ~1, SOCK_DCCP, tp); +#else + msg_Err (obj, "DCCP support not included"); +#endif + break; + + case IPPROTO_TCP: + fd = net_Connect (obj, shost, (sport + 1) & ~1, SOCK_STREAM, tp); + break; + } + free (tmp); if (fd == -1) return VLC_EGENERIC; + net_SetCSCov (fd, -1, 12); /* Initializes demux */ demux_sys_t *p_sys = malloc (sizeof (*p_sys)); if (p_sys == NULL) - goto error; + { + net_Close (fd); + if (rtcp_fd != -1) + net_Close (rtcp_fd); + return VLC_EGENERIC; + } + p_sys->srtp = NULL; + p_sys->fd = fd; + p_sys->rtcp_fd = rtcp_fd; p_sys->caching = var_CreateGetInteger (obj, "rtp-caching"); p_sys->max_src = var_CreateGetInteger (obj, "rtp-max-src"); p_sys->timeout = var_CreateGetInteger (obj, "rtp-timeout"); p_sys->max_dropout = var_CreateGetInteger (obj, "rtp-max-dropout"); p_sys->max_misorder = var_CreateGetInteger (obj, "rtp-max-misorder"); p_sys->autodetect = true; + p_sys->framed_rtp = (tp == IPPROTO_TCP); demux->pf_demux = Demux; demux->pf_control = Control; @@ -160,12 +244,32 @@ static int Open (vlc_object_t *obj) if (p_sys->session == NULL) goto error; - p_sys->fd = fd; + char *key = var_CreateGetNonEmptyString (demux, "srtp-key"); + if (key) + { + p_sys->srtp = srtp_create (SRTP_ENCR_AES_CM, SRTP_AUTH_HMAC_SHA1, 10, + SRTP_PRF_AES_CM, SRTP_RCC_MODE1); + if (p_sys->srtp == NULL) + { + free (key); + goto error; + } + + char *salt = var_CreateGetNonEmptyString (demux, "srtp-salt"); + errno = srtp_setkeystring (p_sys->srtp, key, salt ? salt : ""); + free (salt); + free (key); + if (errno) + { + msg_Err (obj, "bad SRTP key/salt combination (%m)"); + goto error; + } + } + return VLC_SUCCESS; error: - net_Close (fd); - free (p_sys); + Close (obj); return VLC_EGENERIC; } @@ -178,7 +282,12 @@ static void Close (vlc_object_t *obj) demux_t *demux = (demux_t *)obj; demux_sys_t *p_sys = demux->p_sys; - rtp_session_destroy (demux, p_sys->session); + if (p_sys->srtp) + srtp_destroy (p_sys->srtp); + if (p_sys->session) + rtp_session_destroy (demux, p_sys->session); + if (p_sys->rtcp_fd != -1) + net_Close (p_sys->rtcp_fd); net_Close (p_sys->fd); free (p_sys); } @@ -197,7 +306,7 @@ static int extract_port (char **phost) if (host[0] == '[') { - host = *++phost; /* skip '[' */ + host = ++*phost; /* skip '[' */ port = strchr (host, ']'); if (port) *port++ = '\0'; /* skip ']' */ @@ -225,7 +334,7 @@ static int Control (demux_t *demux, int i_query, va_list args) { float *v = va_arg (args, float *); *v = 0.; - return 0; + return VLC_SUCCESS; } case DEMUX_GET_LENGTH: @@ -233,14 +342,23 @@ static int Control (demux_t *demux, int i_query, va_list args) { int64_t *v = va_arg (args, int64_t *); *v = 0; - return 0; + return VLC_SUCCESS; } case DEMUX_GET_PTS_DELAY: { int64_t *v = va_arg (args, int64_t *); - *v = p_sys->caching; - return 0; + *v = p_sys->caching * 1000; + return VLC_SUCCESS; + } + + case DEMUX_CAN_PAUSE: + case DEMUX_CAN_SEEK: + case DEMUX_CAN_CONTROL_PACE: + { + bool *v = (bool*)va_arg( args, bool * ); + *v = false; + return VLC_SUCCESS; } } @@ -249,22 +367,79 @@ static int Control (demux_t *demux, int i_query, va_list args) /** - * Gets a datagram from the network + * Checks if a file descriptor is hung up. + */ +static bool fd_dead (int fd) +{ + struct pollfd ufd = { .fd = fd, }; + + return (poll (&ufd, 1, 0) == 1) && (ufd.revents & POLLHUP); +} + + +/** + * Gets a datagram from the network, or NULL in case of fatal error. */ static block_t *rtp_dgram_recv (demux_t *demux, int fd) { block_t *block = block_Alloc (0xffff); + ssize_t len; - ssize_t len = net_Read (VLC_OBJECT (demux), fd, NULL, - block->p_buffer, block->i_buffer, false); - if (len == -1) + do { - block_Release (block); - return NULL; + len = net_Read (VLC_OBJECT (demux), fd, NULL, + block->p_buffer, block->i_buffer, false); + if (((len <= 0) && fd_dead (fd)) + || !vlc_object_alive (demux)) + { + block_Release (block); + return NULL; + } } + while (len == -1); + return block_Realloc (block, 0, len); } +/** + * Gets a framed RTP packet, or NULL in case of fatal error. + */ +static block_t *rtp_stream_recv (demux_t *demux, int fd) +{ + ssize_t len = 0; + uint8_t hdr[2]; /* frame header */ + + /* Receives the RTP frame header */ + do + { + ssize_t val = net_Read (VLC_OBJECT (demux), fd, NULL, + hdr + len, 2 - len, false); + if (val <= 0) + return NULL; + len += val; + } + while (len < 2); + + block_t *block = block_Alloc (GetWBE (hdr)); + + /* Receives the RTP packet */ + for (ssize_t i = 0; i < len;) + { + ssize_t val; + + val = net_Read (VLC_OBJECT (demux), fd, NULL, + block->p_buffer + i, block->i_buffer - i, false); + if (val <= 0) + { + block_Release (block); + return NULL; + } + i += val; + } + + return block; +} + /* * Generic packet handlers @@ -287,8 +462,7 @@ static void codec_decode (demux_t *demux, void *data, block_t *block) if (data) { block->i_dts = 0; /* RTP does not specify this */ - es_out_Control (demux->out, ES_OUT_SET_PCR, - block->i_pts - demux->p_sys->caching * 1000); + es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts ); es_out_Send (demux->out, (es_out_id_t *)data, block); } else @@ -322,6 +496,55 @@ static void stream_decode (demux_t *demux, void *data, block_t *block) * Static payload types handler */ +/* PT=0 + * PCMU: G.711 µ-law (RFC3551) + */ +static void *pcmu_init (demux_t *demux) +{ + es_format_t fmt; + + es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC ('u', 'l', 'a', 'w')); + fmt.audio.i_rate = 8000; + fmt.audio.i_channels = 1; + return codec_init (demux, &fmt); +} + +/* PT=8 + * PCMA: G.711 A-law (RFC3551) + */ +static void *pcma_init (demux_t *demux) +{ + es_format_t fmt; + + es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC ('a', 'l', 'a', 'w')); + fmt.audio.i_rate = 8000; + fmt.audio.i_channels = 1; + return codec_init (demux, &fmt); +} + +/* PT=10,11 + * L16: 16-bits (network byte order) PCM + */ +static void *l16s_init (demux_t *demux) +{ + es_format_t fmt; + + es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC ('s', '1', '6', 'b')); + fmt.audio.i_rate = 44100; + fmt.audio.i_channels = 2; + return codec_init (demux, &fmt); +} + +static void *l16m_init (demux_t *demux) +{ + es_format_t fmt; + + es_format_Init (&fmt, AUDIO_ES, VLC_FOURCC ('s', '1', '6', 'b')); + fmt.audio.i_rate = 44100; + fmt.audio.i_channels = 1; + return codec_init (demux, &fmt); +} + /* PT=14 * MPA: MPEG Audio (RFC2250, §3.4) */ @@ -386,7 +609,7 @@ static void mpv_decode (demux_t *demux, void *data, block_t *block) */ static void *ts_init (demux_t *demux) { - return stream_init (demux, "ts"); + return stream_init (demux, *demux->psz_demux ? demux->psz_demux : "ts"); } @@ -403,59 +626,99 @@ static int Demux (demux_t *demux) demux_sys_t *p_sys = demux->p_sys; block_t *block; - block = rtp_dgram_recv (demux, p_sys->fd); - if (block) - { - /* Not using SDP, we need to guess the payload format used */ - if (p_sys->autodetect && block->i_buffer >= 2) - { - rtp_pt_t pt = { - .init = NULL, - .destroy = codec_destroy, - .decode = codec_decode, - .frequency = 0, - .number = block->p_buffer[1] & 0x7f, - }; - - switch (pt.number) - { - case 14: - msg_Dbg (demux, "detected MPEG Audio over RTP"); - pt.init = mpa_init; - pt.decode = mpa_decode; - pt.frequency = 44100; - break; + block = p_sys->framed_rtp + ? rtp_stream_recv (demux, p_sys->fd) + : rtp_dgram_recv (demux, p_sys->fd); + if (!block) + return 0; - case 32: - msg_Dbg (demux, "detected MPEG Video over RTP"); - pt.init = mpv_init; - pt.decode = mpv_decode; - pt.frequency = 90000; - break; + if (block->i_buffer < 2) + goto drop; - case 33: - msg_Dbg (demux, "detected MPEG2 TS over RTP"); - pt.init = ts_init; - pt.destroy = stream_destroy; - pt.decode = stream_decode; - pt.frequency = 90000; - break; + const uint8_t ptype = block->p_buffer[1] & 0x7F; + if (ptype >= 72 && ptype <= 76) + goto drop; /* Muxed RTCP, ignore for now */ - case 72: /* muxed SR */ - case 73: /* muxed RR */ - case 74: /* muxed SDES */ - case 75: /* muxed BYE */ - case 76: /* muxed APP */ - default: - block_Release (block); /* ooh! ignoring RTCP is evil! */ - return 1; - } - rtp_add_type (demux, p_sys->session, &pt); - p_sys->autodetect = false; + if (p_sys->srtp) + { + size_t len = block->i_buffer; + if (srtp_recv (p_sys->srtp, block->p_buffer, &len)) + { + msg_Dbg (demux, "SRTP authentication/decryption failed"); + goto drop; } + block->i_buffer = len; + } - rtp_receive (demux, p_sys->session, block); + /* Not using SDP, we need to guess the payload format used */ + /* see http://www.iana.org/assignments/rtp-parameters */ + if (p_sys->autodetect) + { + rtp_pt_t pt = { + .init = NULL, + .destroy = codec_destroy, + .decode = codec_decode, + .frequency = 0, + .number = ptype, + }; + switch (ptype) + { + case 0: + msg_Dbg (demux, "detected G.711 mu-law"); + pt.init = pcmu_init; + pt.frequency = 8000; + break; + + case 8: + msg_Dbg (demux, "detected G.711 A-law"); + pt.init = pcma_init; + pt.frequency = 8000; + break; + + case 10: + msg_Dbg (demux, "detected stereo PCM"); + pt.init = l16s_init; + pt.frequency = 44100; + break; + + case 11: + msg_Dbg (demux, "detected mono PCM"); + pt.init = l16m_init; + pt.frequency = 44100; + break; + + case 14: + msg_Dbg (demux, "detected MPEG Audio"); + pt.init = mpa_init; + pt.decode = mpa_decode; + pt.frequency = 90000; + break; + + case 32: + msg_Dbg (demux, "detected MPEG Video"); + pt.init = mpv_init; + pt.decode = mpv_decode; + pt.frequency = 90000; + break; + + case 33: + msg_Dbg (demux, "detected MPEG2 TS"); + pt.init = ts_init; + pt.destroy = stream_destroy; + pt.decode = stream_decode; + pt.frequency = 90000; + break; + + default: + goto drop; + } + rtp_add_type (demux, p_sys->session, &pt); + p_sys->autodetect = false; } + rtp_receive (demux, p_sys->session, block); + return 1; +drop: + block_Release (block); return 1; }