Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits: 65632fe6 by Sergio Ammirata at 2021-07-10T09:28:35+00:00 Remove old RIST modules - - - - - e6dd5f6a by Sergio Ammirata at 2021-07-10T09:28:35+00:00 Upgrade meson to 0.56.2 (needed to build librist) - - - - - ba63f304 by Sergio Ammirata at 2021-07-10T09:28:35+00:00 Add librist to contrib Upgrade librist contrib to v0.2.4 - - - - - 1fa33c18 by Sergio Ammirata at 2021-07-10T09:28:35+00:00 Add libRIST based access and access_output modules. These support the new RIST main profile as well as the RIST simple profile. Co-authored-by: Gijs Peskens <[email protected]> - - - - - 10 changed files: - configure.ac - + contrib/src/librist/SHA512SUMS - + contrib/src/librist/rules.mak - extras/tools/SHA512SUMS - extras/tools/packages.mak - modules/access/Makefile.am - modules/access/rist.c - modules/access/rist.h - modules/access_output/Makefile.am - modules/access_output/rist.c Changes: ===================================== configure.ac ===================================== @@ -4273,6 +4273,11 @@ dnl SRT plugin dnl PKG_ENABLE_MODULES_VLC([SRT], [access_srt access_output_srt], [srt >= 1.3.0], [SRT input/output plugin], [auto], [], [], [-DENABLE_SRT]) +dnl +dnl RIST plugin +dnl +PKG_ENABLE_MODULES_VLC([RIST], [rist access_output_rist], [librist], [RIST input/output plugin (default auto)]) + EXTEND_HELP_STRING([Visualisations and Video filter plugins:]) dnl dnl goom visualization plugin ===================================== contrib/src/librist/SHA512SUMS ===================================== @@ -0,0 +1 @@ +1c45f87bb0f0ea5a7acb529a796c546190cb5b461297930f2fad214fe0a84c76771158564d9e0ac92e8882e5e008b406655940b3babb5d198e68816cbba4bc68 librist-v0.2.4.tar.gz ===================================== contrib/src/librist/rules.mak ===================================== @@ -0,0 +1,29 @@ +# librist + +LIBRIST_VERSION := v0.2.4 +LIBRIST_URL := http://code.videolan.org/rist/librist/-/archive/$(LIBRIST_VERSION)/librist-$(LIBRIST_VERSION).tar.gz + +ifdef BUILD_NETWORK +PKGS += librist +endif + +ifeq ($(call need_pkg,"librist >= 0.2"),) +PKGS_FOUND += librist +endif + +LIBRIST_CONF = -Dbuilt_tools=false -Dtest=false + +$(TARBALLS)/librist-$(LIBRIST_VERSION).tar.gz: + $(call download_pkg,$(LIBRIST_URL),librist) + +.sum-librist: librist-$(LIBRIST_VERSION).tar.gz + +librist: librist-$(LIBRIST_VERSION).tar.gz .sum-librist + $(UNPACK) + $(MOVE) + +.librist: librist crossfile.meson + cd $< && rm -rf ./build + cd $< && $(HOSTVARS_MESON) $(MESON) $(LIBRIST_CONF) build + cd $< && cd build && ninja install + touch $@ ===================================== extras/tools/SHA512SUMS ===================================== @@ -18,4 +18,4 @@ e9785f3d620a204b7d20222888917dc065c2036cae28667065bf7862dfa1b25235095a12fd04efdb d24849b93de58b20f518c071687e7bfa653a96600382f36c4cf7fc1047656458f75f093b911b786b18b6931b2453cb60868ecbe07cc7d2984e5981a874b34942 help2man-1.47.6.tar.xz 8d23dde18525dccaa648ca01df40151e7f00cec4846bd611c8970dbcfc1fb57a453facfe4d41462e7c3c8bb548d44b961a04e4fc3073ab6b65063e53f42bf6fd nasm-2.14.tar.gz 1650bf9e3eddeb0b0fbb415c2b8e0a7c094421e991fa8139fd77fae0f6ee7ee980b7cf5e98d883c3a884f99abcb06fa26e3980af3a3a5bb6dd655124755782c2 ninja-1.8.2.tar.gz -172b4de8c7474d709f172431b89bf2b2b1c2c38bc842039cccf6be075a45bd3509a1dab8512bc5b2ee025d65d8050d2f717dd15c1f9be17fca3b2e7da0d3e889 meson-0.55.1.tar.gz +3b6cc5cae31d756b251ecde3483d3710bceff50cfd03ef6cf6f939d9e599998e61fcb03a2ee09d6a6f9bfa2198f43e7f20447359de3bff1055febcf03e82e514 meson-0.56.2.tar.gz ===================================== extras/tools/packages.mak ===================================== @@ -57,7 +57,7 @@ GETTEXT_URL=$(GNU)/gettext/gettext-$(GETTEXT_VERSION).tar.gz HELP2MAN_VERSION=1.47.6 HELP2MAN_URL=$(GNU)/help2man/help2man-$(HELP2MAN_VERSION).tar.xz -MESON_VERSION=0.55.1 +MESON_VERSION=0.56.2 MESON_URL=https://github.com/mesonbuild/meson/releases/download/$(MESON_VERSION)/meson-$(MESON_VERSION).tar.gz NINJA_VERSION=1.8.2 ===================================== modules/access/Makefile.am ===================================== @@ -449,8 +449,8 @@ EXTRA_LTLIBRARIES += libaccess_srt_plugin.la ### RIST ### librist_plugin_la_SOURCES = access/rist.c access/rist.h -librist_plugin_la_CFLAGS = $(AM_CFLAGS) $(BITSTREAM_CFLAGS) -librist_plugin_la_LIBADD = $(SOCKET_LIBS) -if HAVE_BITSTREAM -access_LTLIBRARIES += librist_plugin.la -endif +librist_plugin_la_CFLAGS = $(AM_CFLAGS) $(RIST_CFLAGS) +librist_plugin_la_LIBADD = $(RIST_LIBS) $(SOCKET_LIBS) $(LIBPTHREAD) +librist_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)' +access_LTLIBRARIES += $(LTLIBrist) +EXTRA_LTLIBRARIES += librist_plugin.la ===================================== modules/access/rist.c ===================================== @@ -1,11 +1,9 @@ /***************************************************************************** * rist.c: RIST (Reliable Internet Stream Transport) input module ***************************************************************************** - * Copyright (C) 2018, DVEO, the Broadcast Division of Computer Modules, Inc. - * Copyright (C) 2018, SipRadius LLC + * Copyright (C) 2021, SipRadius LLC * * Authors: Sergio Ammirata <[email protected]> - * Daniele Lacamera <[email protected]> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by @@ -30,1024 +28,195 @@ #include <vlc_interrupt.h> #include <vlc_plugin.h> #include <vlc_access.h> -#include <vlc_queue.h> -#include <vlc_threads.h> -#include <vlc_network.h> #include <vlc_block.h> -#include <vlc_url.h> -#ifdef HAVE_POLL_H -#include <poll.h> -#endif -#include <bitstream/ietf/rtcp_rr.h> -#include <bitstream/ietf/rtcp_sdes.h> -#include <bitstream/ietf/rtcp_fb.h> -#include <bitstream/ietf/rtp.h> +#define RIST_CFG_PREFIX "rist-" #include "rist.h" -/* The default latency is 1000 ms */ -#define RIST_DEFAULT_LATENCY 1000 -/* The default nack retry interval */ -#define RIST_DEFAULT_RETRY_INTERVAL 132 -/* The default packet re-ordering buffer */ -#define RIST_DEFAULT_REORDER_BUFFER 70 -/* The default max packet size */ -#define RIST_MAX_PACKET_SIZE 1472 -/* The default timeout is 5 ms */ -#define RIST_DEFAULT_POLL_TIMEOUT 5 -/* The max retry count for nacks */ -#define RIST_MAX_RETRIES 10 -/* The rate at which we process and send nack requests */ -#define NACK_INTERVAL 5 /*ms*/ -/* Calculate and print stats once per second */ -#define STATS_INTERVAL 1000 /*ms*/ +#define NACK_FMT_RANGE 0 +#define NACK_FMT_BITMASK 1 -static const int nack_type[] = { - 0, 1, +static const int nack_type_values[] = { + NACK_FMT_RANGE, NACK_FMT_BITMASK, }; static const char *const nack_type_names[] = { N_("Range"), N_("Bitmask"), }; -enum NACK_TYPE { - NACK_FMT_RANGE = 0, - NACK_FMT_BITMASK -}; - typedef struct { - struct rist_flow *flow; - char sender_name[MAX_CNAME]; - enum NACK_TYPE nack_type; - uint64_t last_data_rx; - uint64_t last_nack_tx; - vlc_thread_t thread; - int i_max_packet_size; - int i_poll_timeout; - int i_poll_timeout_current; - bool b_ismulticast; - bool b_sendnacks; - bool b_sendblindnacks; - bool b_disablenacks; - bool b_flag_discontinuity; - bool dead; - vlc_queue_t queue; - vlc_mutex_t lock; - uint64_t last_message; - uint64_t last_reset; - /* stat variables */ - uint32_t i_poll_timeout_zero_count; - uint32_t i_poll_timeout_nonzero_count; - uint64_t i_last_stat; - float vbr_ratio; - uint16_t vbr_ratio_count; - uint32_t i_lost_packets; - uint32_t i_nack_packets; - uint32_t i_recovered_packets; - uint32_t i_reordered_packets; - uint32_t i_total_packets; + struct rist_ctx *receiver_ctx; + int gre_filter_dst_port; + uint32_t cumulative_loss; + uint32_t flow_id; + bool eof; + int i_recovery_buffer; + int i_maximum_jitter; + struct rist_logging_settings logging_settings; + vlc_mutex_t lock; + struct rist_data_block *rist_items[RIST_MAX_QUEUE_BUFFERS]; } stream_sys_t; -static int Control(stream_t *p_access, int i_query, va_list args) -{ - switch( i_query ) - { - case STREAM_CAN_SEEK: - case STREAM_CAN_FASTSEEK: - case STREAM_CAN_PAUSE: - case STREAM_CAN_CONTROL_PACE: - *va_arg( args, bool * ) = false; - break; - - case STREAM_GET_PTS_DELAY: - *va_arg( args, vlc_tick_t * ) = VLC_TICK_FROM_MS( - var_InheritInteger(p_access, "network-caching") ); - break; - - default: - return VLC_EGENERIC; - } - - return VLC_SUCCESS; -} - -static struct rist_flow *rist_init_rx(void) -{ - struct rist_flow *flow = calloc(1, sizeof(struct rist_flow)); - if (!flow) - return NULL; - - flow->reset = 1; - flow->buffer = calloc(RIST_QUEUE_SIZE, sizeof(struct rtp_pkt)); - - if ( unlikely( flow->buffer == NULL ) ) - { - free(flow); - return NULL; - } - flow->fd_in = -1; - flow->fd_nack = -1; - flow->fd_rtcp_m = -1; - - return flow; -} - -static void rist_WriteTo_i11e_Locked(vlc_mutex_t lock, int fd, const void *buf, size_t len, - const struct sockaddr *peer, socklen_t slen) -{ - vlc_mutex_lock( &lock ); - rist_WriteTo_i11e(fd, buf, len, peer, slen); - vlc_mutex_unlock( &lock ); -} - -static struct rist_flow *rist_udp_receiver(stream_t *p_access, vlc_url_t *parsed_url, bool b_ismulticast) -{ - stream_sys_t *p_sys = p_access->p_sys; - msg_Info( p_access, "Opening Rist Flow Receiver at %s:%d and %s:%d", - parsed_url->psz_host, parsed_url->i_port, - parsed_url->psz_host, parsed_url->i_port+1); - - p_sys->flow = rist_init_rx(); - if (!p_sys->flow) - return NULL; - - p_sys->flow->fd_in = net_OpenDgram(p_access, parsed_url->psz_host, parsed_url->i_port, NULL, - 0, IPPROTO_UDP); - if (p_sys->flow->fd_in < 0) - { - msg_Err( p_access, "cannot open input socket" ); - goto fail; - } - - if (b_ismulticast) - { - p_sys->flow->fd_rtcp_m = net_OpenDgram(p_access, parsed_url->psz_host, parsed_url->i_port + 1, - NULL, 0, IPPROTO_UDP); - if (p_sys->flow->fd_rtcp_m < 0) - { - msg_Err( p_access, "cannot open multicast nack socket" ); - goto fail; - } - p_sys->flow->fd_nack = net_ConnectDgram(p_access, parsed_url->psz_host, - parsed_url->i_port + 1, -1, IPPROTO_UDP ); - } - else - { - p_sys->flow->fd_nack = net_OpenDgram(p_access, parsed_url->psz_host, parsed_url->i_port + 1, - NULL, 0, IPPROTO_UDP); - } - if (p_sys->flow->fd_nack < 0) - { - msg_Err( p_access, "cannot open nack socket" ); - goto fail; - } - - populate_cname(p_sys->flow->fd_nack, p_sys->flow->cname); - msg_Info(p_access, "our cname is %s", p_sys->flow->cname); - - return p_sys->flow; - -fail: - if (p_sys->flow->fd_in != -1) - vlc_close(p_sys->flow->fd_in); - if (p_sys->flow->fd_nack != -1) - vlc_close(p_sys->flow->fd_nack); - if (p_sys->flow->fd_rtcp_m != -1) - vlc_close(p_sys->flow->fd_rtcp_m); - free(p_sys->flow->buffer); - free(p_sys->flow); - return NULL; -} - -static int is_index_in_range(struct rist_flow *flow, uint16_t idx) -{ - if (flow->ri <= flow->wi) { - return ((idx > flow->ri) && (idx <= flow->wi)); - } else { - return ((idx > flow->ri) || (idx <= flow->wi)); - } -} - -static void send_rtcp_feedback(stream_t *p_access, struct rist_flow *flow) +static int cb_stats(void *arg, const struct rist_stats *stats_container) { + stream_t *p_access = (stream_t*)arg; stream_sys_t *p_sys = p_access->p_sys; - int namelen = strlen(flow->cname) + 1; - /* we need to make sure it is a multiple of 4, pad if necessary */ - if ((namelen - 2) & 0x3) - namelen = ((((namelen - 2) >> 2) + 1) << 2) + 2; + msg_Dbg(p_access, "[RIST-STATS]: %s", stats_container->stats_json); - int rtcp_feedback_size = RTCP_EMPTY_RR_SIZE + RTCP_SDES_SIZE + namelen; - uint8_t *buf = malloc(rtcp_feedback_size); - if ( unlikely( buf == NULL ) ) - return; + const struct rist_stats_receiver_flow *stats_receiver_flow = &stats_container->stats.receiver_flow; - /* Populate RR */ - uint8_t *rr = buf; - rtp_set_hdr(rr); - rtcp_rr_set_pt(rr); - rtcp_set_length(rr, 1); - rtcp_fb_set_int_ssrc_pkt_sender(rr, 0); + p_sys->cumulative_loss += stats_receiver_flow->lost; + msg_Dbg(p_access, "[RIST-STATS]: received %"PRIu64", missing %"PRIu32", reordered %"PRIu32", recovered %"PRIu32", lost %"PRIu32", Q %.2f, max jitter (us) %"PRIu64", rtt %"PRIu32"ms, cumulative loss %"PRIu32"", + stats_receiver_flow->received, + stats_receiver_flow->missing, + stats_receiver_flow->reordered, + stats_receiver_flow->recovered, + stats_receiver_flow->lost, + stats_receiver_flow->quality, + stats_receiver_flow->max_inter_packet_spacing, + stats_receiver_flow->rtt, + p_sys->cumulative_loss + ); - /* Populate SDES */ - uint8_t *p_sdes = (buf + RTCP_EMPTY_RR_SIZE); - rtp_set_hdr(p_sdes); - rtp_set_cc(p_sdes, 1); /* Actually it is source count in this case */ - rtcp_sdes_set_pt(p_sdes); - rtcp_set_length(p_sdes, (namelen >> 2) + 2); - rtcp_sdes_set_cname(p_sdes, 1); - rtcp_sdes_set_name_length(p_sdes, strlen(flow->cname)); - uint8_t *p_sdes_name = (buf + RTCP_EMPTY_RR_SIZE + RTCP_SDES_SIZE); - strlcpy((char *)p_sdes_name, flow->cname, namelen); - - /* Write to Socket */ - rist_WriteTo_i11e_Locked(p_sys->lock, flow->fd_nack, buf, rtcp_feedback_size, - (struct sockaddr *)&flow->peer_sockaddr, flow->peer_socklen); - free(buf); - buf = NULL; -} - -static void send_bbnack(stream_t *p_access, int fd_nack, block_t *pkt_nacks, uint16_t nack_count) -{ - stream_sys_t *p_sys = p_access->p_sys; - struct rist_flow *flow = p_sys->flow; - int len = 0; - - int bbnack_bufsize = RTCP_FB_HEADER_SIZE + - RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count; - uint8_t *buf = malloc(bbnack_bufsize); - if ( unlikely( buf == NULL ) ) - return; - - /* Populate NACKS */ - uint8_t *nack = buf; - rtp_set_hdr(nack); - rtcp_fb_set_fmt(nack, NACK_FMT_BITMASK); - rtcp_set_pt(nack, RTCP_PT_RTPFB); - rtcp_set_length(nack, 2 + nack_count); - /*uint8_t name[4] = "RIST";*/ - /*rtcp_fb_set_ssrc_media_src(nack, name);*/ - len += RTCP_FB_HEADER_SIZE; - /* TODO : group together */ - uint16_t nacks[MAX_NACKS]; - memcpy(nacks, pkt_nacks->p_buffer, pkt_nacks->i_buffer); - for (int i = 0; i < nack_count; i++) { - uint8_t *nack_record = buf + len + RTCP_FB_FCI_GENERIC_NACK_SIZE*i; - rtcp_fb_nack_set_packet_id(nack_record, nacks[i]); - rtcp_fb_nack_set_bitmask_lost(nack_record, 0); - } - len += RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count; - - /* Write to Socket */ - if (p_sys->b_sendnacks && p_sys->b_disablenacks == false) - rist_WriteTo_i11e_Locked(p_sys->lock, fd_nack, buf, len, - (struct sockaddr *)&flow->peer_sockaddr, flow->peer_socklen); - free(buf); - buf = NULL; -} - -static void send_rbnack(stream_t *p_access, int fd_nack, block_t *pkt_nacks, uint16_t nack_count) -{ - stream_sys_t *p_sys = p_access->p_sys; - struct rist_flow *flow = p_sys->flow; - int len = 0; - - int rbnack_bufsize = RTCP_FB_HEADER_SIZE + - RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count; - uint8_t *buf = malloc(rbnack_bufsize); - if ( unlikely( buf == NULL ) ) - return; - - /* Populate NACKS */ - uint8_t *nack = buf; - rtp_set_hdr(nack); - rtcp_fb_set_fmt(nack, NACK_FMT_RANGE); - rtcp_set_pt(nack, RTCP_PT_RTPFR); - rtcp_set_length(nack, 2 + nack_count); - uint8_t name[4] = "RIST"; - rtcp_fb_set_ssrc_media_src(nack, name); - len += RTCP_FB_HEADER_SIZE; - /* TODO : group together */ - uint16_t nacks[MAX_NACKS]; - memcpy(nacks, pkt_nacks->p_buffer, pkt_nacks->i_buffer); - for (int i = 0; i < nack_count; i++) + if ((int)stats_receiver_flow->max_inter_packet_spacing > p_sys->i_recovery_buffer * 1000) { - uint8_t *nack_record = buf + len + RTCP_FB_FCI_GENERIC_NACK_SIZE*i; - rtcp_fb_nack_set_range_start(nack_record, nacks[i]); - rtcp_fb_nack_set_range_extra(nack_record, 0); + msg_Err(p_access, "The IP network jitter exceeded your recovery buffer size, %d > %d us, you should increase the recovery buffer size or fix your source/network jitter", + (int)stats_receiver_flow->max_inter_packet_spacing, p_sys->i_recovery_buffer *1000); } - len += RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count; - - /* Write to Socket */ - if (p_sys->b_sendnacks && p_sys->b_disablenacks == false) - rist_WriteTo_i11e_Locked(p_sys->lock, fd_nack, buf, len, - (struct sockaddr *)&flow->peer_sockaddr, flow->peer_socklen); - free(buf); - buf = NULL; -} -static void send_nacks(stream_t *p_access, struct rist_flow *flow) -{ - stream_sys_t *p_sys = p_access->p_sys; - struct rtp_pkt *pkt; - uint16_t idx; - uint64_t last_ts = 0; - uint16_t null_count = 0; - int nacks_len = 0; - uint16_t nacks[MAX_NACKS]; - - idx = flow->ri; - while(idx++ != flow->wi) - { - pkt = &(flow->buffer[idx]); - if (pkt->buffer == NULL) - { - if (nacks_len + 1 >= MAX_NACKS) - { - break; - } - else - { - null_count++; - /* TODO: after adding average spacing calculation, change this formula - to extrapolated_ts = last_ts + null_count * avg_delta_ts; */ - uint64_t extrapolated_ts = last_ts; - /* Find out the age and add it only if necessary */ - int retry_count = flow->nacks_retries[idx]; - uint64_t age = flow->hi_timestamp - extrapolated_ts; - uint64_t expiration; - if (retry_count == 0){ - expiration = flow->reorder_buffer; - } else { - expiration = (uint64_t)flow->nacks_retries[idx] * (uint64_t)flow->retry_interval; - } - if (age > expiration && retry_count <= flow->max_retries) - { - flow->nacks_retries[idx]++; - nacks[nacks_len++] = idx; - msg_Dbg(p_access, "Sending NACK for seq %d, age %"PRId64" ms, retry %u, " \ - "expiration %"PRId64" ms", idx, ts_get_from_rtp(age)/1000, - flow->nacks_retries[idx], ts_get_from_rtp(expiration)/1000); - } - } - } - else - { - last_ts = pkt->rtp_ts; - null_count = 0; - } - } - if (nacks_len > 0) + if ((int)stats_receiver_flow->rtt > (p_sys->i_recovery_buffer)) { - p_sys->i_nack_packets += nacks_len; - block_t *pkt_nacks = block_Alloc(nacks_len * 2); - if (pkt_nacks) - { - memcpy(pkt_nacks->p_buffer, nacks, nacks_len * 2); - pkt_nacks->i_buffer = nacks_len * 2; - vlc_queue_Enqueue(&p_sys->queue, pkt_nacks); - } + msg_Err(p_access, "The RTT between us and the sender is higher than the configured recovery buffer size, %"PRIu32" > %d ms, you should increase the recovery buffer size", + stats_receiver_flow->rtt, p_sys->i_recovery_buffer); } -} - -static int sockaddr_cmp(struct sockaddr *x, struct sockaddr *y) -{ -#define CMP(a, b) if (a != b) return a < b ? -1 : 1 - CMP(x->sa_family, y->sa_family); - if (x->sa_family == AF_INET) - { - struct sockaddr_in *xin = (void*)x, *yin = (void*)y; - CMP(ntohl(xin->sin_addr.s_addr), ntohl(yin->sin_addr.s_addr)); - CMP(ntohs(xin->sin_port), ntohs(yin->sin_port)); - } - else if (x->sa_family == AF_INET6) - { - struct sockaddr_in6 *xin6 = (void*)x, *yin6 = (void*)y; - int r = memcmp(xin6->sin6_addr.s6_addr, yin6->sin6_addr.s6_addr, - sizeof(xin6->sin6_addr.s6_addr)); - if (r != 0) - return r; - CMP(ntohs(xin6->sin6_port), ntohs(yin6->sin6_port)); - CMP(xin6->sin6_flowinfo, yin6->sin6_flowinfo); - CMP(xin6->sin6_scope_id, yin6->sin6_scope_id); + vlc_mutex_lock( &p_sys->lock ); + /* Trigger the appropriate response when there is no more data */ + /* status of 1 is no data for one buffer length */ + /* status of 2 is no data for 60 seconds, i.e. session timeout */ + if (p_sys->flow_id == stats_receiver_flow->flow_id && stats_receiver_flow->status == 2) { + p_sys->eof = true; } + vlc_mutex_unlock( &p_sys->lock ); -#undef CMP + rist_stats_free(stats_container); return 0; } -static void print_sockaddr_info_change(stream_t *p_access, struct sockaddr *x, struct sockaddr *y) -{ - if (x->sa_family == AF_INET) - { - struct sockaddr_in *xin = (void*)x, *yin = (void*)y; - msg_Info(p_access, "Peer IP:Port change detected: old IP:Port %s:%d, new IP:Port %s:%d", - inet_ntoa(xin->sin_addr), ntohs(xin->sin_port), inet_ntoa(yin->sin_addr), - ntohs(yin->sin_port)); - } - else if (x->sa_family == AF_INET6) - { - struct sockaddr_in6 *xin6 = (void*)x, *yin6 = (void*)y; - char oldstr[INET6_ADDRSTRLEN]; - char newstr[INET6_ADDRSTRLEN]; - inet_ntop(xin6->sin6_family, &xin6->sin6_addr, oldstr, sizeof(struct in6_addr)); - inet_ntop(yin6->sin6_family, &yin6->sin6_addr, newstr, sizeof(struct in6_addr)); - msg_Info(p_access, "Peer IP:Port change detected: old IP:Port %s:%d, new IP:Port %s:%d", - oldstr, ntohs(xin6->sin6_port), newstr, ntohs(yin6->sin6_port)); - } -} - -static void print_sockaddr_info(stream_t *p_access, struct sockaddr *x) -{ - if (x->sa_family == AF_INET) - { - struct sockaddr_in *xin = (void*)x; - msg_Info(p_access, "Peer IP:Port %s:%d", inet_ntoa(xin->sin_addr), ntohs(xin->sin_port)); - } - else if (x->sa_family == AF_INET6) - { - struct sockaddr_in6 *xin6 = (void*)x; - char str[INET6_ADDRSTRLEN]; - inet_ntop(xin6->sin6_family, &xin6->sin6_addr, str, sizeof(struct in6_addr)); - msg_Info(p_access, "Peer IP:Port %s:%d", str, ntohs(xin6->sin6_port)); - } -} - -static void rtcp_input(stream_t *p_access, struct rist_flow *flow, uint8_t *buf_in, size_t len, - struct sockaddr *peer, socklen_t slen) -{ - stream_sys_t *p_sys = p_access->p_sys; - uint8_t ptype; - size_t processed_bytes = 0; - uint16_t records; - char new_sender_name[MAX_CNAME]; - uint8_t *buf; - - while (processed_bytes < len) { - buf = buf_in + processed_bytes; - /* safety checks */ - size_t bytes_left = len - processed_bytes + 1; - if ( bytes_left < 4 ) - { - /* we must have at least 4 bytes */ - msg_Err(p_access, "Rist rtcp packet must have at least 4 bytes, we have %zu", - bytes_left); - return; - } - else if (!rtp_check_hdr(buf)) - { - /* check for a valid rtp header */ - msg_Err(p_access, "Malformed rtcp packet starting with %02x, ignoring.", buf[0]); - return; - } - - ptype = rtcp_get_pt(buf); - records = rtcp_get_length(buf); - uint16_t bytes = (uint16_t)(4 * (1 + records)); - if (bytes > bytes_left) - { - /* check for a sane number of bytes */ - msg_Err(p_access, "Malformed rtcp packet, wrong len %d, expecting %u bytes in the " \ - "packet, got a buffer of %zu bytes.", rtcp_get_length(buf), bytes, bytes_left); - return; - } - - switch(ptype) { - case RTCP_PT_RTPFR: - case RTCP_PT_RTPFB: - break; - - case RTCP_PT_RR: - break; - - case RTCP_PT_SDES: - { - if (p_sys->b_sendnacks == false) - p_sys->b_sendnacks = true; - if (p_sys->b_ismulticast) - return; - /* Check for changes in source IP address or port */ - uint8_t name_length = rtcp_sdes_get_name_length(buf); - if (name_length > bytes_left || - name_length > sizeof(new_sender_name)) - { - /* check for a sane number of bytes */ - msg_Err(p_access, "Malformed SDES packet, wrong cname len %"PRIu8", got a " \ - "buffer of %zu bytes.", name_length, bytes_left); - return; - } - bool ip_port_changed = false; - if (sockaddr_cmp((struct sockaddr *)&flow->peer_sockaddr, peer) != 0) - { - ip_port_changed = true; - if(flow->peer_socklen > 0) - print_sockaddr_info_change(p_access, - (struct sockaddr *)&flow->peer_sockaddr, peer); - else - print_sockaddr_info(p_access, peer); - vlc_mutex_lock( &p_sys->lock ); - memcpy(&flow->peer_sockaddr, peer, sizeof(struct sockaddr_storage)); - flow->peer_socklen = slen; - vlc_mutex_unlock( &p_sys->lock ); - } - - /* Check for changes in cname */ - bool peer_name_changed = false; - memset(new_sender_name, 0, MAX_CNAME); - memcpy(new_sender_name, buf + RTCP_SDES_SIZE, name_length); - if (memcmp(new_sender_name, p_sys->sender_name, name_length) != 0) - { - peer_name_changed = true; - if (strcmp(p_sys->sender_name, "") == 0) - msg_Info(p_access, "Peer Name: %s", new_sender_name); - else - msg_Info(p_access, "Peer Name change detected: old Name: %s, new " \ - "Name: %s", p_sys->sender_name, new_sender_name); - memset(p_sys->sender_name, 0, MAX_CNAME); - memcpy(p_sys->sender_name, buf + RTCP_SDES_SIZE, name_length); - } - - /* Reset the buffer as the source must have been restarted */ - if (peer_name_changed || ip_port_changed) - { - /* reset the buffer */ - flow->reset = 1; - } - } - break; - - case RTCP_PT_SR: - if (p_sys->b_sendnacks == false) - p_sys->b_sendnacks = true; - if (p_sys->b_ismulticast) - return; - break; - - default: - msg_Err(p_access, " Unrecognized RTCP packet with PTYPE=%02x!!", ptype); - } - processed_bytes += (4 * (1 + records)); - } -} - -static bool rist_input(stream_t *p_access, struct rist_flow *flow, uint8_t *buf, size_t len) +static int Control(stream_t *p_access, int i_query, va_list args) { - stream_sys_t *p_sys = p_access->p_sys; - - /* safety checks */ - if ( len < RTP_HEADER_SIZE ) - { - /* check if packet size >= rtp header size */ - msg_Err(p_access, "Rist rtp packet must have at least 12 bytes, we have %zu", len); - return false; - } - else if (!rtp_check_hdr(buf)) - { - /* check for a valid rtp header */ - msg_Err(p_access, "Malformed rtp packet header starting with %02x, ignoring.", buf[0]); - return false; - } - - uint16_t idx = rtp_get_seqnum(buf); - uint32_t pkt_ts = rtp_get_timestamp(buf); - bool retrasnmitted = false; - bool success = true; - - if (flow->reset == 1) - { - msg_Info(p_access, "Traffic detected after buffer reset"); - /* First packet in the queue */ - flow->hi_timestamp = pkt_ts; - msg_Info(p_access, "ts@%u", flow->hi_timestamp); - flow->wi = idx; - flow->ri = idx; - flow->reset = 0; - p_sys->b_flag_discontinuity = true; - } - - /* Check to see if this is a retransmission or a regular packet */ - if (buf[11] & (1 << 0)) - { - msg_Dbg(p_access, "Packet %d RECOVERED, Window: [%d:%d-->%d]", idx, flow->ri, flow->wi, - flow->wi-flow->ri); - p_sys->i_recovered_packets++; - retrasnmitted = true; - } - else if (flow->wi != flow->ri) - { - /* Reset counter to 0 on incoming holes */ - /* Regular packets only as retransmits are expected to come in out of order */ - uint16_t idxnext = (uint16_t)(flow->wi + 1); - if (idx != idxnext) - { - if (idx > idxnext) { - msg_Dbg(p_access, "Gap, got %d, expected %d, %d packet gap, Window: [%d:%d-->%d]", - idx, idxnext, idx - idxnext, flow->ri, flow->wi, (uint16_t)(flow->wi-flow->ri)); - } else { - p_sys->i_reordered_packets++; - msg_Dbg(p_access, "Out of order, got %d, expected %d, Window: [%d:%d-->%d]", idx, - idxnext, flow->ri, flow->wi, (uint16_t)(flow->wi-flow->ri)); - } - uint16_t zero_counter = (uint16_t)(flow->wi + 1); - while(zero_counter++ != idx) { - flow->nacks_retries[zero_counter] = 0; - } - /*msg_Dbg(p_access, "Gap, reseting %d packets as zero nacks %d to %d", - idx - flow->wi - 1, (uint16_t)(flow->wi + 1), idx);*/ - } - } - - /* Always replace the existing one with the new one */ - struct rtp_pkt *pkt; - pkt = &(flow->buffer[idx]); - if (pkt->buffer && pkt->buffer->i_buffer > 0) + switch( i_query ) { - block_Release(pkt->buffer); - pkt->buffer = NULL; - } - pkt->buffer = block_Alloc(len); - if (!pkt->buffer) - return false; - - pkt->buffer->i_buffer = len; - memcpy(pkt->buffer->p_buffer, buf, len); - pkt->rtp_ts = pkt_ts; - p_sys->last_data_rx = vlc_tick_now(); - /* Reset the try counter regardless of wether it was a retransmit or not */ - flow->nacks_retries[idx] = 0; + case STREAM_CAN_SEEK: + case STREAM_CAN_FASTSEEK: + case STREAM_CAN_PAUSE: + case STREAM_CAN_CONTROL_PACE: + *va_arg( args, bool * ) = false; + break; - if (retrasnmitted) - return success; + case STREAM_GET_PTS_DELAY: + *va_arg( args, vlc_tick_t * ) = VLC_TICK_FROM_MS( + var_InheritInteger(p_access, "network-caching") ); + break; - p_sys->i_total_packets++; - /* Perform discontinuity checks and udpdate counters */ - if (!is_index_in_range(flow, idx) && pkt_ts >= flow->hi_timestamp) - { - if ((pkt_ts - flow->hi_timestamp) > flow->hi_timestamp/10) - { - msg_Info(p_access, "Forward stream discontinuity idx@%d/%d/%d ts@%u/%u", flow->ri, idx, - flow->wi, pkt_ts, flow->hi_timestamp); - flow->reset = 1; - success = false; - } - else - { - flow->wi = idx; - flow->hi_timestamp = pkt_ts; - } - } - else if (!is_index_in_range(flow, idx)) - { - /* incoming timestamp just jumped back in time or index is outside of scope */ - msg_Info(p_access, "Backwards stream discontinuity idx@%d/%d/%d ts@%u/%u", flow->ri, idx, - flow->wi, pkt_ts, flow->hi_timestamp); - flow->reset = 1; - success = false; + default: + return VLC_EGENERIC; } - return success; + return VLC_SUCCESS; } -static block_t *rist_dequeue(stream_t *p_access, struct rist_flow *flow) +static block_t *BlockRIST(stream_t *p_access, bool *restrict eof) { stream_sys_t *p_sys = p_access->p_sys; block_t *pktout = NULL; - struct rtp_pkt *pkt; - uint16_t idx; - if (flow->ri == flow->wi || flow->reset > 0) - return NULL; - - idx = flow->ri; - bool found_data = false; - uint16_t loss_amount = 0; - while(idx++ != flow->wi) { + struct rist_data_block *rist_buffer = NULL; + size_t i_total_size, i_rist_items_index; + int i_flags, ret; + *eof = false; + i_rist_items_index = i_flags = i_total_size = 0; + int i_read_timeout_ms = p_sys->i_maximum_jitter; - pkt = &(flow->buffer[idx]); - if (!pkt->buffer) - { - /*msg_Info(p_access, "Possible packet loss on index #%d", idx);*/ - loss_amount++; - /* We move ahead until we find a timestamp but we do not move the cursor. - * None of them are guaranteed packet loss because we do not really - * know their timestamps. They might still arrive on the next loop. - * We can confirm the loss only if we get a valid packet in the loop below. */ + while ((ret = rist_receiver_data_read2(p_sys->receiver_ctx, &rist_buffer, i_read_timeout_ms)) > 0) + { + if (p_sys->gre_filter_dst_port > 0 && rist_buffer->virt_dst_port != p_sys->gre_filter_dst_port) { + rist_receiver_data_block_free2(&rist_buffer); continue; } - - /*printf("IDX=%d, flow->hi_timestamp: %u, (ts + flow->rtp_latency): %u\n", idx, - flow->hi_timestamp, (ts - 100 * flow->qdelay));*/ - if (flow->hi_timestamp > (uint32_t)(pkt->rtp_ts + flow->rtp_latency)) - { - /* Populate output packet now but remove rtp header from source */ - int newSize = pkt->buffer->i_buffer - RTP_HEADER_SIZE; - pktout = block_Alloc(newSize); - if (pktout) - { - pktout->i_buffer = newSize; - memcpy(pktout->p_buffer, pkt->buffer->p_buffer + RTP_HEADER_SIZE, newSize); - /* free the buffer and increase the read index */ - flow->ri = idx; - /* TODO: calculate average duration using buffer average (bring from sender) */ - found_data = true; + i_read_timeout_ms = 0; + + p_sys->rist_items[i_rist_items_index++] = rist_buffer; + i_total_size += rist_buffer->payload_len; + vlc_mutex_lock( &p_sys->lock ); + if (p_sys->flow_id != rist_buffer->flow_id || + rist_buffer->flags == RIST_DATA_FLAGS_DISCONTINUITY || + rist_buffer->flags == RIST_DATA_FLAGS_FLOW_BUFFER_START) { + if (p_sys->flow_id != rist_buffer->flow_id) { + msg_Info(p_access, "New flow detected with id %"PRIu32"", rist_buffer->flow_id); + p_sys->flow_id = rist_buffer->flow_id; } - block_Release(pkt->buffer); - pkt->buffer = NULL; + i_flags = BLOCK_FLAG_DISCONTINUITY; + vlc_mutex_unlock( &p_sys->lock ); break; } - - } - - if (loss_amount > 0 && found_data == true) - { - /* Packet loss confirmed, we found valid data after the holes */ - msg_Dbg(p_access, "Packet NOT RECOVERED, %d packet(s), Window: [%d:%d]", loss_amount, - flow->ri, flow->wi); - p_sys->i_lost_packets += loss_amount; - p_sys->b_flag_discontinuity = true; - } - - return pktout; -} - -static void *rist_thread(void *data) -{ - stream_t *p_access = data; - stream_sys_t *p_sys = p_access->p_sys; - block_t *pkt_nacks; - - /* Process nacks every 5ms */ - /* We only ask for the relevant ones */ - while ((pkt_nacks = vlc_queue_DequeueKillable(&p_sys->queue, - &p_sys->dead)) != NULL) { - /* there are two bytes per nack */ - uint16_t nack_count = (uint16_t)pkt_nacks->i_buffer/2; - switch(p_sys->nack_type) { - case NACK_FMT_BITMASK: - send_bbnack(p_access, p_sys->flow->fd_nack, pkt_nacks, nack_count); - break; - - default: - send_rbnack(p_access, p_sys->flow->fd_nack, pkt_nacks, nack_count); - } - - if (nack_count > 1) - msg_Dbg(p_access, "Sent %u NACKs !!!", nack_count); - block_Release(pkt_nacks); + vlc_mutex_unlock( &p_sys->lock ); + // Make sure we never read more than our array size + if (i_rist_items_index == (RIST_MAX_QUEUE_BUFFERS -1)) + break; } - return NULL; -} - -static block_t *BlockRIST(stream_t *p_access, bool *restrict eof) -{ - stream_sys_t *p_sys = p_access->p_sys; - uint64_t now; - *eof = false; - block_t *pktout = NULL; - struct pollfd pfd[3]; - int ret; - ssize_t r; - struct sockaddr_storage peer; - socklen_t slen = sizeof(struct sockaddr_storage); - struct rist_flow *flow = p_sys->flow; + if (ret > 50) + msg_Dbg(p_access, "Falling behind reading rist buffer by %d packets", ret); - if (vlc_killed()) - { + if (ret < 0) { + msg_Err(p_access, "Unrecoverable error %i while reading from rist, ending stream", ret); *eof = true; - return NULL; + goto failed_cleanup; } - int poll_sockets = 2; - pfd[0].fd = flow->fd_in; - pfd[0].events = POLLIN; - pfd[1].fd = flow->fd_nack; - pfd[1].events = POLLIN; - if (p_sys->b_ismulticast) - { - pfd[2].fd = flow->fd_rtcp_m; - pfd[2].events = POLLIN; - poll_sockets++; - } - - /* The protocol uses a fifo buffer with a fixed time delay. - * That buffer needs to be emptied at a rate that is determined by the rtp timestamps of the - * packets. If I waited indefinitely for data coming in, the rate and delay of output packets - * would be wrong. I am calling the rist_dequeue function every time a data packet comes in - * and also every time we get a poll timeout. The configurable poll timeout is for controling - * the maximum jitter of output data coming out of the buffer. The default 5ms timeout covers - * most cases. */ - - ret = vlc_poll_i11e(pfd, poll_sockets, p_sys->i_poll_timeout_current); - if (unlikely(ret < 0)) + if (i_total_size == 0) { return NULL; - else if (ret == 0) - { - /* Poll timeout, check the queue for the next packet that needs to be delivered */ - pktout = rist_dequeue(p_access, flow); - /* if there is data, we need to come back faster to finish emptying it */ - if (pktout) { - p_sys->i_poll_timeout_current = 0; - p_sys->i_poll_timeout_zero_count++; - } else { - p_sys->i_poll_timeout_current = p_sys->i_poll_timeout; - p_sys->i_poll_timeout_nonzero_count++; - } - } - else - { - - uint8_t *buf = malloc(p_sys->i_max_packet_size); - if ( unlikely( buf == NULL ) ) - return NULL; - - /* Process rctp incoming data */ - if (pfd[1].revents & POLLIN) - { - r = rist_ReadFrom_i11e(flow->fd_nack, buf, p_sys->i_max_packet_size, - (struct sockaddr *)&peer, &slen); - if (unlikely(r == -1)) { - msg_Err(p_access, "socket %d error: %s\n", flow->fd_nack, gai_strerror(errno)); - } - else { - if (p_sys->b_ismulticast == false) - rtcp_input(p_access, flow, buf, r, (struct sockaddr *)&peer, slen); - } - } - if (p_sys->b_ismulticast && pfd[2].revents & POLLIN) - { - r = rist_ReadFrom_i11e(flow->fd_rtcp_m, buf, p_sys->i_max_packet_size, - (struct sockaddr *)&peer, &slen); - if (unlikely(r == -1)) { - msg_Err(p_access, "mcast socket %d error: %s\n",flow->fd_rtcp_m, gai_strerror(errno)); - } - else { - rtcp_input(p_access, flow, buf, r, (struct sockaddr *)&peer, slen); - } - } - - /* Process regular incoming data */ - if (pfd[0].revents & POLLIN) - { - r = rist_Read_i11e(flow->fd_in, buf, p_sys->i_max_packet_size); - if (unlikely(r == -1)) { - msg_Err(p_access, "socket %d error: %s\n", flow->fd_in, gai_strerror(errno)); - } - else - { - /* rist_input will process and queue the pkt */ - if (rist_input(p_access, flow, buf, r)) - { - /* Check the queue for the next packet that needs to be delivered */ - pktout = rist_dequeue(p_access, flow); - if (pktout) { - p_sys->i_poll_timeout_current = 0; - p_sys->i_poll_timeout_zero_count++; - } else { - p_sys->i_poll_timeout_current = p_sys->i_poll_timeout; - p_sys->i_poll_timeout_nonzero_count++; - } - } - } - } - - free(buf); - buf = NULL; } - now = vlc_tick_now(); - - /* Process stats and print them out */ - /* We need to measure some items every 70ms */ - uint64_t interval = (now - flow->feedback_time); - if ( interval > VLC_TICK_FROM_MS(RTCP_INTERVAL) ) - { - if (p_sys->i_poll_timeout_nonzero_count > 0) - { - float ratio = (float)p_sys->i_poll_timeout_zero_count - / (float)p_sys->i_poll_timeout_nonzero_count; - if (ratio <= 1) - p_sys->vbr_ratio += 1 - ratio; - else - p_sys->vbr_ratio += ratio - 1; - p_sys->vbr_ratio_count++; - /*msg_Dbg(p_access, "zero poll %u, non-zero poll %u, ratio %.2f", - p_sys->i_poll_timeout_zero_count, p_sys->i_poll_timeout_nonzero_count, ratio);*/ - p_sys->i_poll_timeout_zero_count = 0; - p_sys->i_poll_timeout_nonzero_count = 0; - } - } - /* We print out the stats once per second */ - interval = (now - p_sys->i_last_stat); - if ( interval > VLC_TICK_FROM_MS(STATS_INTERVAL) ) - { - if ( p_sys->i_lost_packets > 0) - msg_Err(p_access, "We have %d lost packets", p_sys->i_lost_packets); - float ratio = 1; - if (p_sys->vbr_ratio_count > 0) - ratio = p_sys->vbr_ratio / (float)p_sys->vbr_ratio_count; - float quality = 100; - if (p_sys->i_total_packets > 0) - quality -= (float)100*(float)(p_sys->i_lost_packets + p_sys->i_recovered_packets + - p_sys->i_reordered_packets)/(float)p_sys->i_total_packets; - if (quality != 100) - msg_Info(p_access, "STATS: Total %u, Recovered %u/%u, Reordered %u, Lost %u, VBR " \ - "Score %.2f, Link Quality %.2f%%", p_sys->i_total_packets, - p_sys->i_recovered_packets, p_sys->i_nack_packets, p_sys->i_reordered_packets, - p_sys->i_lost_packets, ratio, quality); - p_sys->i_last_stat = now; - p_sys->vbr_ratio = 0; - p_sys->vbr_ratio_count = 0; - p_sys->i_lost_packets = 0; - p_sys->i_nack_packets = 0; - p_sys->i_recovered_packets = 0; - p_sys->i_reordered_packets = 0; - p_sys->i_total_packets = 0; + // Prepare one large buffer (when we are behing in reading, otherwise it is the same size as what is being read) + pktout = block_Alloc(i_total_size); + if ( unlikely(pktout == NULL) ) { + goto failed_cleanup; } - /* Send rtcp feedback every RTCP_INTERVAL */ - interval = (now - flow->feedback_time); - if ( interval > VLC_TICK_FROM_MS(RTCP_INTERVAL) ) - { - /* msg_Dbg(p_access, "Calling RTCP Feedback %lu<%d ms using timer", interval, - VLC_TICK_FROM_MS(RTCP_INTERVAL)); */ - send_rtcp_feedback(p_access, flow); - flow->feedback_time = now; - } - - /* Send nacks every NACK_INTERVAL (only the ones that have matured, if any) */ - interval = (now - p_sys->last_nack_tx); - if ( interval > VLC_TICK_FROM_MS(NACK_INTERVAL) ) - { - send_nacks(p_access, p_sys->flow); - p_sys->last_nack_tx = now; - } - - /* Safety check for when the input stream stalls */ - if ( p_sys->last_data_rx > 0 && now > p_sys->last_data_rx && - (uint64_t)(now - p_sys->last_data_rx) > (uint64_t)VLC_TICK_FROM_MS(flow->latency) && - (uint64_t)(now - p_sys->last_reset) > (uint64_t)VLC_TICK_FROM_MS(flow->latency) ) - { - msg_Err(p_access, "No data received for %"PRId64" ms, resetting buffers", - (int64_t)(now - p_sys->last_data_rx)/1000); - p_sys->last_reset = now; - flow->reset = 1; + size_t block_offset = 0; + for(size_t i = 0; i < i_rist_items_index; ++i) { + memcpy(pktout->p_buffer + block_offset, p_sys->rist_items[i]->payload, p_sys->rist_items[i]->payload_len); + block_offset += p_sys->rist_items[i]->payload_len; + rist_receiver_data_block_free2(& p_sys->rist_items[i]); } + pktout->i_flags = i_flags; + return pktout; - if (pktout) - { - if (p_sys->b_flag_discontinuity) { - pktout->i_flags |= BLOCK_FLAG_DISCONTINUITY; - p_sys->b_flag_discontinuity = false; +failed_cleanup: + if (i_total_size > 0) { + for (size_t i = 0; i < i_rist_items_index; i++) { + rist_receiver_data_block_free2(&p_sys->rist_items[i]); } - return pktout; } - else - return NULL; + return NULL; } -static void Clean( stream_t *p_access ) -{ - stream_sys_t *p_sys = p_access->p_sys; - - if (p_sys->flow) - { - if (p_sys->flow->fd_in >= 0) - net_Close (p_sys->flow->fd_in); - if (p_sys->flow->fd_nack >= 0) - net_Close (p_sys->flow->fd_nack); - if (p_sys->flow->fd_rtcp_m >= 0) - net_Close (p_sys->flow->fd_rtcp_m); - for (int i=0; i<RIST_QUEUE_SIZE; i++) { - struct rtp_pkt *pkt = &(p_sys->flow->buffer[i]); - if (pkt->buffer && pkt->buffer->i_buffer > 0) { - block_Release(pkt->buffer); - pkt->buffer = NULL; - } - } - free(p_sys->flow->buffer); - free(p_sys->flow); - } -} static void Close(vlc_object_t *p_this) { - stream_t *p_access = (stream_t*)p_this; + stream_t *p_access = (stream_t*)p_this; stream_sys_t *p_sys = p_access->p_sys; - - vlc_queue_Kill(&p_sys->queue, &p_sys->dead); - vlc_join(p_sys->thread, NULL); - - Clean( p_access ); + rist_destroy(p_sys->receiver_ctx); } static int Open(vlc_object_t *p_this) { stream_t *p_access = (stream_t*)p_this; stream_sys_t *p_sys = NULL; - vlc_url_t parsed_url = { 0 }; p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) ); if( unlikely( p_sys == NULL ) ) @@ -1057,55 +226,64 @@ static int Open(vlc_object_t *p_this) vlc_mutex_init( &p_sys->lock ); - if ( vlc_UrlParse( &parsed_url, p_access->psz_url ) == -1 ) - { - msg_Err( p_access, "Failed to parse input URL (%s)", - p_access->psz_url ); - goto failed; + int rist_profile = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_PROFILE); + p_sys->i_maximum_jitter = var_InheritInteger(p_access, RIST_CFG_PREFIX "maximum-jitter"); + p_sys->gre_filter_dst_port = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_DST_PORT); + if (p_sys->gre_filter_dst_port % 2 != 0) { + msg_Err(p_access, "Virtual destination port must be an even number."); + return VLC_EGENERIC; } + if (rist_profile == RIST_PROFILE_SIMPLE) + p_sys->gre_filter_dst_port = 0; - /* Initialize rist flow */ - p_sys->b_ismulticast = is_multicast_address(parsed_url.psz_host); - p_sys->flow = rist_udp_receiver(p_access, &parsed_url, p_sys->b_ismulticast); - vlc_UrlClean( &parsed_url ); - if (!p_sys->flow) - { - msg_Err( p_access, "Failed to open rist flow (%s)", - p_access->psz_url ); - goto failed; + int i_recovery_length = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_CFG_LATENCY); + if (i_recovery_length == 0) { + // Auto-configure the recovery buffer + i_recovery_length = 1000;//1 Second, libRIST default } + p_sys->i_recovery_buffer = i_recovery_length; - p_sys->b_flag_discontinuity = false; - p_sys->b_disablenacks = var_InheritBool( p_access, "disable-nacks" ); - p_sys->b_sendblindnacks = var_InheritBool( p_access, "mcast-blind-nacks" ); - if (p_sys->b_sendblindnacks && p_sys->b_disablenacks == false) - p_sys->b_sendnacks = true; - else - p_sys->b_sendnacks = false; - p_sys->nack_type = var_InheritInteger( p_access, "nack-type" ); - p_sys->i_max_packet_size = var_InheritInteger( p_access, "packet-size" ); - p_sys->i_poll_timeout = var_InheritInteger( p_access, "maximum-jitter" ); - p_sys->flow->retry_interval = var_InheritInteger( p_access, "retry-interval" ); - p_sys->flow->max_retries = var_InheritInteger( p_access, "max-retries" ); - p_sys->flow->latency = var_InheritInteger( p_access, "latency" ); - if (p_sys->b_disablenacks) - p_sys->flow->reorder_buffer = p_sys->flow->latency; - else - p_sys->flow->reorder_buffer = var_InheritInteger( p_access, "reorder-buffer" ); - msg_Info(p_access, "Setting queue latency to %d ms", p_sys->flow->latency); + int i_verbose_level = var_InheritInteger( p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VERBOSE_LEVEL ); - /* Convert to rtp times */ - p_sys->flow->rtp_latency = rtp_get_ts(VLC_TICK_FROM_MS(p_sys->flow->latency)); - p_sys->flow->retry_interval = rtp_get_ts(VLC_TICK_FROM_MS(p_sys->flow->retry_interval)); - p_sys->flow->reorder_buffer = rtp_get_ts(VLC_TICK_FROM_MS(p_sys->flow->reorder_buffer)); + //This simply disables the global logs, which are only used by the udpsocket functions provided + //by libRIST. When called by the library logs are anyway generated (though perhaps less accurate). + //This prevents all sorts of complications wrt other rist modules running and other the like. + struct rist_logging_settings rist_global_logging_settings = LOGGING_SETTINGS_INITIALIZER; + if (rist_logging_set_global(&rist_global_logging_settings) != 0) { + msg_Err(p_access,"Could not set logging\n"); + return VLC_EGENERIC; + } - p_sys->dead = false; - vlc_queue_Init(&p_sys->queue, offsetof (block_t, p_next)); + struct rist_logging_settings *logging_settings = &p_sys->logging_settings; + logging_settings->log_socket = -1; + logging_settings->log_stream = NULL; + logging_settings->log_level = i_verbose_level; + logging_settings->log_cb = log_cb; + logging_settings->log_cb_arg = p_access; - /* This extra thread is for sending feedback/nack packets even when no data comes in */ - if (vlc_clone(&p_sys->thread, rist_thread, p_access, VLC_THREAD_PRIORITY_INPUT)) - { - msg_Err(p_access, "Failed to create worker thread."); + if (rist_receiver_create(&p_sys->receiver_ctx, rist_profile, logging_settings) != 0) { + msg_Err(p_access, "Could not create rist receiver context"); + return VLC_EGENERIC; + } + + int nack_type = var_InheritInteger( p_access, RIST_CFG_PREFIX "nack-type" ); + if (rist_receiver_nack_type_set(p_sys->receiver_ctx, nack_type)) { + msg_Err(p_access, "Could not set nack type"); + goto failed; + } + + // Enable stats data + if (rist_stats_callback_set(p_sys->receiver_ctx, 1000, cb_stats, (void *)p_access) == -1) { + msg_Err(p_access, "Could not enable stats callback"); + goto failed; + } + + if( !rist_add_peers((vlc_object_t *)p_access, p_sys->receiver_ctx, p_access->psz_url, 0, RIST_DEFAULT_VIRT_DST_PORT, i_recovery_length) ) + goto failed; + + /* Start the rist protocol thread */ + if (rist_start(p_sys->receiver_ctx)) { + msg_Err(p_access, "Could not start rist receiver"); goto failed; } @@ -1115,10 +293,16 @@ static int Open(vlc_object_t *p_this) return VLC_SUCCESS; failed: - Clean( p_access ); + rist_destroy(p_sys->receiver_ctx); + msg_Err(p_access, "Failed to open rist module"); return VLC_EGENERIC; } +#define DST_PORT_TEXT N_("Virtual Destination Port Filter") +#define DST_PORT_LONGTEXT N_( \ + "Destination port to be used inside the reduced-mode of the main profile "\ + "to filter incoming data. Use zero to allow all." ) + /* Module descriptor */ vlc_module_begin () @@ -1127,30 +311,54 @@ vlc_module_begin () set_category( CAT_INPUT ) set_subcategory( SUBCAT_INPUT_ACCESS ) - add_integer( "packet-size", RIST_MAX_PACKET_SIZE, - N_("RIST maximum packet size (bytes)"), NULL ) - add_integer( "maximum-jitter", RIST_DEFAULT_POLL_TIMEOUT, + add_integer( RIST_CFG_PREFIX "maximum-jitter", 5, N_("RIST demux/decode maximum jitter (default is 5ms)"), N_("This controls the maximum jitter that will be passed to the demux/decode chain. " - "The lower the value, the more CPU cycles the algorithm will consume") ) - add_integer( "latency", RIST_DEFAULT_LATENCY, N_("RIST latency (ms)"), NULL ) - add_integer( "retry-interval", RIST_DEFAULT_RETRY_INTERVAL, N_("RIST nack retry interval (ms)"), - NULL ) - add_integer( "reorder-buffer", RIST_DEFAULT_REORDER_BUFFER, N_("RIST reorder buffer (ms)"), - NULL ) - add_integer( "max-retries", RIST_MAX_RETRIES, N_("RIST maximum retry count"), NULL ) - add_integer( "nack-type", NACK_FMT_RANGE, - N_("RIST nack type, 0 = range, 1 = bitmask. Default is range"), NULL ) - change_integer_list( nack_type, nack_type_names ) - add_bool( "disable-nacks", false, N_("Disable NACK output packets"), - N_("Use this to disable packet recovery") ) - add_bool( "mcast-blind-nacks", false, N_("Do not check for a valid rtcp message from the encoder"), - N_("Send nack messages even when we have not confirmed that the encoder is on our local " \ - "network.") ) - - set_capability( "access", 0 ) - add_shortcut( "rist", "tr06" ) + "The lower the value, the more CPU cycles the module will consume")) + add_integer( RIST_CFG_PREFIX "nack-type", NACK_FMT_RANGE, + N_("RIST nack type, 0 = range, 1 = bitmask. Default is range"), NULL) + change_integer_list( nack_type_values, nack_type_names ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_DST_PORT, 0, + DST_PORT_TEXT, DST_PORT_LONGTEXT ) + add_integer( RIST_CFG_PREFIX RIST_CFG_MAX_PACKET_SIZE, RIST_MAX_PACKET_SIZE, + RIST_PACKET_SIZE_TEXT, NULL ) + add_string( RIST_CFG_PREFIX RIST_CFG_URL2, NULL, RIST_URL2_TEXT, NULL ) + add_string( RIST_CFG_PREFIX RIST_CFG_URL3, NULL, RIST_URL3_TEXT, NULL ) + add_string( RIST_CFG_PREFIX RIST_CFG_URL4, NULL, RIST_URL4_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_BANDWIDTH, RIST_DEFAULT_RECOVERY_MAXBITRATE, + RIST_MAX_BITRATE_TEXT, RIST_MAX_BITRATE_LONGTEXT ) + add_integer( RIST_CFG_PREFIX RIST_CFG_RETRY_INTERVAL, RIST_DEFAULT_RECOVERY_RTT_MIN, + RIST_RETRY_INTERVAL_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_REORDER_BUFFER, RIST_DEFAULT_RECOVERY_REORDER_BUFFER, + RIST_REORDER_BUFFER_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_CFG_MAX_RETRIES, RIST_DEFAULT_MAX_RETRIES, + RIST_MAX_RETRIES_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VERBOSE_LEVEL, RIST_DEFAULT_VERBOSE_LEVEL, + RIST_VERBOSE_LEVEL_TEXT, RIST_VERBOSE_LEVEL_LONGTEXT ) + change_integer_list( verbose_level_type, verbose_level_type_names ) + add_integer( RIST_CFG_PREFIX RIST_CFG_LATENCY, 0, + BUFFER_TEXT, BUFFER_LONGTEXT ) + add_string( RIST_CFG_PREFIX RIST_URL_PARAM_CNAME, NULL, RIST_CNAME_TEXT, + RIST_CNAME_LONGTEXT ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_PROFILE, RIST_DEFAULT_PROFILE, + RIST_PROFILE_TEXT, RIST_PROFILE_LONGTEXT ) + add_password( RIST_CFG_PREFIX RIST_URL_PARAM_SECRET, "", + RIST_SHARED_SECRET_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_AES_TYPE, 0, + RIST_ENCRYPTION_TYPE_TEXT, NULL ) + change_integer_list( rist_encryption_type, rist_encryption_type_names ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_TIMING_MODE, RIST_DEFAULT_TIMING_MODE, + RIST_TIMING_MODE_TEXT, NULL ) + change_integer_list( rist_timing_mode_type, rist_timing_mode_names ) + add_string( RIST_CFG_PREFIX RIST_URL_PARAM_SRP_USERNAME, "", + RIST_SRP_USERNAME_TEXT, NULL ) + add_password( RIST_CFG_PREFIX RIST_URL_PARAM_SRP_PASSWORD, "", + RIST_SRP_PASSWORD_TEXT, NULL ) + + set_capability( "access", 10 ) + add_shortcut( "librist", "rist", "tr06" ) set_callbacks( Open, Close ) vlc_module_end () + ===================================== modules/access/rist.h ===================================== @@ -1,11 +1,9 @@ /***************************************************************************** * rist.h: RIST (Reliable Internet Stream Transport) helper ***************************************************************************** - * Copyright (C) 2018, DVEO, the Broadcast Division of Computer Modules, Inc. - * Copyright (C) 2018, SipRadius LLC + * Copyright (C) 2020, SipRadius LLC * * Authors: Sergio Ammirata <[email protected]> - * Daniele Lacamera <[email protected]> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by @@ -22,346 +20,223 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#include <stdint.h> -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#include <errno.h> -#include <assert.h> +#ifndef RIST_VLC_COMMON_H +#define RIST_VLC_COMMON_H 1 -/***************************************************************************** - * Public API - *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -/* RIST */ - -/* RTP header format (RFC 3550) */ -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P|X| CC |M| PT | sequence number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | synchronization source (SSRC) identifier | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | contributing source (CSRC) identifiers | - | .... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -#define RIST_QUEUE_SIZE 65536 -#define RTP_PKT_SIZE (1472) - -#define RTCP_INTERVAL 75 /*ms*/ - -#define SEVENTY_YEARS_OFFSET (2208988800ULL) -#define MAX_NACKS 32 -#define MAX_CNAME 128 -#define RTCP_EMPTY_RR_SIZE 8 - -#define RTCP_PT_RTPFR 204 - -struct rtp_pkt { - uint32_t rtp_ts; - struct block_t *buffer; +#include <vlc_common.h> +#include <librist/librist.h> + +/* RIST max fifo count (private RIST_DATAOUT_QUEUE_BUFFERS in librist) */ +#define RIST_MAX_QUEUE_BUFFERS (1024) + +/* RIST parameter names */ +#define RIST_CFG_MAX_PACKET_SIZE "packet-size" +#define RIST_CFG_URL2 "peer-url2" +#define RIST_CFG_URL3 "peer-url3" +#define RIST_CFG_URL4 "peer-url4" +#define RIST_CFG_RETRY_INTERVAL "retry-interval" +#define RIST_CFG_MAX_RETRIES "max-retries" +#define RIST_CFG_LATENCY "latency" + +static const char *rist_log_label[9] = {"DISABLED", "", "", "ERROR", "WARN", "", "INFO", "DEBUG", "SIMULATE"}; + +/* RIST parameter descriptions */ +#define RIST_URL2_TEXT N_("Second peer URL") +#define RIST_URL3_TEXT N_("Third peer URL") +#define RIST_URL4_TEXT N_("Fourth peer URL") +#define RIST_MAX_BITRATE_TEXT N_("Max bitrate in Kbps") +#define RIST_MAX_BITRATE_LONGTEXT N_( \ + "Use this value to guarantee that data+retries bitrate never exceeds your pipe size. " \ + "Default value is 100000 Kbps (100 Mbps)" ) +#define RIST_RETRY_INTERVAL_TEXT N_("RIST nack minimum retry interval (ms)") +#define RIST_REORDER_BUFFER_TEXT N_("RIST reorder buffer size (ms)") +#define RIST_MAX_RETRIES_TEXT N_("RIST maximum retry count") +#define BUFFER_TEXT N_("RIST retry-buffer queue size (ms)") +#define BUFFER_LONGTEXT N_( \ + "This must match the buffer size (latency) configured on the other side. If you " \ + "are not sure, leave it blank and it will use 1000ms" ) +#define RIST_SHARED_SECRET_TEXT N_("Shared Secret") +#define RIST_SHARED_SECRET_LONGTEXT N_( \ + "This shared secret is a passphare shared between sender and receiver. The AES key " \ + "is derived from it" ) +#define RIST_CNAME_TEXT N_("Peer cname") +#define RIST_CNAME_LONGTEXT N_( \ + "This name will be sent using the rist RTCP channel and uniquely identifies us" ) +#define RIST_PACKET_SIZE_TEXT N_("RIST maximum packet size (bytes)") +#define RIST_SRP_USERNAME_TEXT N_("Username used for stream authentication") +#define RIST_SRP_PASSWORD_TEXT N_("Password used for stream authentication") +/* Profile selection */ +#define RIST_PROFILE_TEXT N_("Rist Profile") +#define RIST_PROFILE_LONGTEXT N_( "Select the rist profile to use" ) +static const int rist_profile[] = { 0, 1, 2 }; +static const char *const rist_profile_names[] = { + N_("Simple Profile"), N_("Main Profile"), N_("Advanced Profile"), }; - -/* RIST NACK header format */ -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SNBase low bits | Length recovery | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| PT recovery | Mask | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | TS recovery | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |N|D|type |index| Offset | NA |SNBase ext bits| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -struct rist_flow { - uint8_t reset; - struct rtp_pkt *buffer; - uint32_t qsize; - uint32_t last_out; - uint32_t ssrc; - char cname[MAX_CNAME]; - struct sockaddr_storage peer_sockaddr; - socklen_t peer_socklen; - uint16_t ri, wi; - int fd_in; - int fd_out; - int fd_rtcp; - int fd_rtcp_m; - int fd_nack; - uint8_t nacks_retries[RIST_QUEUE_SIZE]; - uint32_t hi_timestamp; - uint64_t feedback_time; - uint32_t latency; - uint32_t rtp_latency; - uint32_t retry_interval; - uint32_t reorder_buffer; - uint8_t max_retries; - uint32_t packets_count; - uint32_t bytes_count; +/* Encryption type */ +#define RIST_ENCRYPTION_TYPE_TEXT N_("Encryption type") +#define RIST_DEFAULT_ENCRYPTION_TPE 128 +static const int rist_encryption_type[] = { 0, 128, 256, }; +static const char *const rist_encryption_type_names[] = { + N_("Disabled"), N_("AES 128 bits"), N_("AES 256 bits"), +}; +/* Timing Mode */ +#define RIST_TIMING_MODE_TEXT N_("Timing mode") +static const int rist_timing_mode_type[] = { 0, 1, 2, }; +static const char *const rist_timing_mode_names[] = { + N_("Use Source Time"), N_("Use Arrival Time"), N_("Use RTC"), +}; +/* Verbose level */ +#define RIST_VERBOSE_LEVEL_TEXT N_("Verbose level") +#define RIST_VERBOSE_LEVEL_LONGTEXT N_("This controls how much log data the library will output over stderr") +static const int verbose_level_type[] = { RIST_LOG_DISABLE, RIST_LOG_ERROR, RIST_LOG_WARN, RIST_LOG_INFO, RIST_LOG_DEBUG, RIST_LOG_SIMULATE }; +static const char *const verbose_level_type_names[] = { + N_("Quiet"), N_("Errors"), N_("Warnings"), N_("Info"), N_("Debug"), N_("Simulate-Loss"), }; -static inline uint16_t rtcp_fb_nack_get_range_start(const uint8_t *p_rtcp_fb_nack) -{ - return (p_rtcp_fb_nack[0] << 8) | p_rtcp_fb_nack[1]; -} +/* Max number of peers */ +// This will restrict the use of the library to the configured maximum packet size +#define RIST_MAX_PACKET_SIZE (10000) -static inline uint16_t rtcp_fb_nack_get_range_extra(const uint8_t *p_rtcp_fb_nack) -{ - return (p_rtcp_fb_nack[2] << 8) | p_rtcp_fb_nack[3]; -} +#define RIST_MAX_LOG_BUFFER 512 -static inline void rtcp_fb_nack_set_range_start(uint8_t *p_rtcp_fb_nack, - uint16_t start) -{ - p_rtcp_fb_nack[0] = (start >> 8) & 0xff; - p_rtcp_fb_nack[1] = start & 0xff; -} -static inline void rtcp_fb_nack_set_range_extra(uint8_t *p_rtcp_fb_nack, - uint16_t extra) +static inline int log_cb(void *arg, enum rist_log_level log_level, const char *msg) { - p_rtcp_fb_nack[2] = (extra >> 8) & 0xff; - p_rtcp_fb_nack[3] = extra & 0xff; + vlc_object_t *p_access = (vlc_object_t *)arg; + int label_index = 0; + if (log_level > 8) + label_index = 8; + else if (log_level > 0) + label_index = log_level; + + if (log_level == RIST_LOG_ERROR) + msg_Err(p_access, "[RIST-%s]: %.*s", rist_log_label[label_index], (int)strlen(msg) - 1, msg); + else if (log_level == RIST_LOG_WARN) + msg_Warn(p_access, "[RIST-%s]: %.*s", rist_log_label[label_index], (int)strlen(msg) - 1, msg); + else if (log_level == RIST_LOG_INFO) + //libRIST follows Syslog log priorities and assigns INFO a low prio, VLC gives Info the highest + //so make it warn (debug would be too verbose) + msg_Warn(p_access, "[RIST-%s]: %.*s", rist_log_label[label_index], (int)strlen(msg) - 1, msg); + else if (log_level > RIST_LOG_DEBUG) + msg_Dbg(p_access, "[RIST-%s]: %.*s", rist_log_label[label_index], (int)strlen(msg) - 1, msg); + + return 0; } -static inline void populate_cname(int fd, char *identifier) +static inline int rist_get_max_packet_size(vlc_object_t *p_this) { - /* Set the CNAME Identifier as host@ip:port and fallback to hostname if needed */ - char hostname[MAX_CNAME]; - struct sockaddr_storage peer_sockaddr; - int name_length = 0; - socklen_t peer_socklen = 0; - int ret_hostname = gethostname(hostname, MAX_CNAME); - if (ret_hostname == -1) - snprintf(hostname, MAX_CNAME, "UnknownHost"); - int ret_sockname = getsockname(fd, (struct sockaddr *)&peer_sockaddr, &peer_socklen); - if (ret_sockname == 0) - { - struct sockaddr *peer = (struct sockaddr *)&peer_sockaddr; - if (peer->sa_family == AF_INET) { - struct sockaddr_in *xin = (void*)peer; - name_length = snprintf(identifier, MAX_CNAME, "%s@%s:%u", hostname, - inet_ntoa(xin->sin_addr), ntohs(xin->sin_port)); - if (name_length >= MAX_CNAME) - identifier[MAX_CNAME-1] = 0; - } else if (peer->sa_family == AF_INET6) { - struct sockaddr_in6 *xin6 = (void*)peer; - char str[INET6_ADDRSTRLEN]; - inet_ntop(xin6->sin6_family, &xin6->sin6_addr, str, sizeof(struct in6_addr)); - name_length = snprintf(identifier, MAX_CNAME, "%s@%s:%u", hostname, - str, ntohs(xin6->sin6_port)); - if (name_length >= MAX_CNAME) - identifier[MAX_CNAME-1] = 0; - } - } - if (name_length == 0) + int i_max_packet_size = var_InheritInteger( p_this, RIST_CFG_PREFIX RIST_CFG_MAX_PACKET_SIZE ); + if (i_max_packet_size > RIST_MAX_PACKET_SIZE) { - name_length = snprintf(identifier, MAX_CNAME, "%s", hostname); - if (name_length >= MAX_CNAME) - identifier[MAX_CNAME-1] = 0; + msg_Err(p_this, "The maximum packet size configured is bigger than what the library allows %d > %d, using %d instead", + i_max_packet_size, RIST_MAX_PACKET_SIZE, RIST_MAX_PACKET_SIZE); + i_max_packet_size = RIST_MAX_PACKET_SIZE; } + return i_max_packet_size; } -static inline uint32_t rtp_get_ts( vlc_tick_t i_pts ) +static inline bool rist_add_peers(vlc_object_t *p_this, struct rist_ctx *ctx, char *psz_url, int i_multipeer_mode, int virt_dst_port, int i_recovery_length) { - unsigned i_clock_rate = 90000; - /* This is an overflow-proof way of doing: - * return i_pts * (int64_t)i_clock_rate / CLOCK_FREQ; - * - * NOTE: this plays nice with offsets because the (equivalent) - * calculations are linear. */ - lldiv_t q = lldiv(i_pts, CLOCK_FREQ); - return q.quot * (int64_t)i_clock_rate - + q.rem * (int64_t)i_clock_rate / CLOCK_FREQ; -} -static inline vlc_tick_t ts_get_from_rtp( uint32_t i_rtp_ts ) -{ - unsigned i_clock_rate = 90000; - return (vlc_tick_t)i_rtp_ts * (vlc_tick_t)(CLOCK_FREQ/i_clock_rate); -} -static inline ssize_t rist_ReadFrom_i11e(int fd, void *buf, size_t len, struct sockaddr *peer, - socklen_t *slen) -{ - ssize_t ret = -1; + int i_rist_reorder_buffer = var_InheritInteger( p_this, RIST_CFG_PREFIX RIST_URL_PARAM_REORDER_BUFFER ); + int i_rist_retry_interval = var_InheritInteger( p_this, RIST_CFG_PREFIX RIST_CFG_RETRY_INTERVAL ); + int i_rist_max_retries = var_InheritInteger( p_this, RIST_CFG_PREFIX RIST_CFG_MAX_RETRIES ); + int i_rist_max_bitrate = var_InheritInteger(p_this, RIST_CFG_PREFIX RIST_URL_PARAM_BANDWIDTH); - if (peer == NULL) - ret = vlc_recv_i11e(fd, buf, len, 0); - else - ret = vlc_recvfrom_i11e(fd, buf, len, 0, peer, slen); + char *psz_stream_name = NULL; + psz_stream_name = var_InheritString( p_this, RIST_CFG_PREFIX RIST_URL_PARAM_CNAME ); - if (ret == -1) - { - switch (errno) - { - case EAGAIN: - case EINTR: - if (vlc_killed()) - return ret; - - /* retry one time */ - if (peer == NULL) - ret = vlc_recv_i11e(fd, buf, len, 0); - else - ret = vlc_recvfrom_i11e(fd, buf, len, 0, peer, slen); - default: - break; - } - } - return ret; -} + char *psz_shared_secret = NULL; + psz_shared_secret = var_InheritString( p_this, RIST_CFG_PREFIX RIST_URL_PARAM_SECRET ); -static inline ssize_t rist_Read_i11e(int fd, void *buf, size_t len) -{ - return rist_ReadFrom_i11e(fd, buf, len, NULL, NULL); -} + int i_key_size = var_InheritInteger(p_this, RIST_CFG_PREFIX RIST_URL_PARAM_AES_TYPE); -static inline ssize_t rist_ReadFrom(int fd, void *buf, size_t len, struct sockaddr *peer, - socklen_t *slen) -{ - ssize_t ret = -1; + char *psz_srp_username = NULL; + psz_srp_username = var_InheritString( p_this, RIST_CFG_PREFIX RIST_URL_PARAM_SRP_USERNAME ); + char *psz_srp_password = NULL; + psz_srp_password = var_InheritString( p_this, RIST_CFG_PREFIX RIST_URL_PARAM_SRP_PASSWORD ); - if (peer == NULL) - ret = recv(fd, buf, len, 0); - else - ret = recvfrom(fd, buf, len, 0, peer, slen); + int recovery_mode = RIST_RECOVERY_MODE_TIME; - if (ret == -1) - { - switch (errno) - { - case EAGAIN: - case EINTR: - /* retry one time */ - if (peer == NULL) - ret = recv(fd, buf, len, 0); - else - ret = recvfrom(fd, buf, len, 0, peer, slen); - default: - break; - } - } - return ret; -} + msg_Info( p_this, "Setting retry buffer to %d ms", i_recovery_length ); -static inline ssize_t rist_Read(int fd, void *buf, size_t len) -{ - return rist_ReadFrom(fd, buf, len, NULL, NULL); -} + char *addr[] = { + strdup(psz_url), + var_InheritString( p_this, RIST_CFG_PREFIX RIST_CFG_URL2 ), + var_InheritString( p_this, RIST_CFG_PREFIX RIST_CFG_URL3 ), + var_InheritString( p_this, RIST_CFG_PREFIX RIST_CFG_URL4 ) + }; -static inline ssize_t rist_WriteTo_i11e(int fd, const void *buf, size_t len, - const struct sockaddr *peer, socklen_t slen) -{ -#ifdef _WIN32 - # define ENOBUFS WSAENOBUFS - # define EAGAIN WSAEWOULDBLOCK - # define EWOULDBLOCK WSAEWOULDBLOCK -#endif - ssize_t r = -1; - if (slen == 0) - r = vlc_send_i11e( fd, buf, len, 0 ); - else - r = vlc_sendto_i11e( fd, buf, len, 0, peer, slen ); - if( r == -1 - && net_errno != EAGAIN && net_errno != EWOULDBLOCK - && net_errno != ENOBUFS && net_errno != ENOMEM ) - { - int type; - if (!getsockopt( fd, SOL_SOCKET, SO_TYPE, - &type, &(socklen_t){ sizeof(type) })) - { - if( type == SOCK_DGRAM ) - { - /* ICMP soft error: ignore and retry */ - if (slen == 0) - r = vlc_send_i11e( fd, buf, len, 0 ); - else - r = vlc_sendto_i11e( fd, buf, len, 0, peer, slen ); - } + bool b_peer_alive = false; + for (size_t i = 0; i < ARRAY_SIZE(addr); i++) { + if (addr[i] == NULL) { + continue; + } + else if (addr[i][0] == '\0') { + free(addr[i]); + continue; } - } - return r; -} -static inline ssize_t rist_Write_i11e(int fd, const void *buf, size_t len) -{ - return rist_WriteTo_i11e(fd, buf, len, NULL, 0); -} + struct rist_peer_config app_peer_config = { + .version = RIST_PEER_CONFIG_VERSION, + .virt_dst_port = virt_dst_port, + .recovery_mode = recovery_mode, + .recovery_maxbitrate = (uint32_t)i_rist_max_bitrate, + .recovery_maxbitrate_return = 0, + .recovery_length_min = (uint32_t)i_recovery_length, + .recovery_length_max = (uint32_t)i_recovery_length, + .recovery_reorder_buffer = (uint32_t)i_rist_reorder_buffer, + .recovery_rtt_min = (uint32_t)i_rist_retry_interval, + .recovery_rtt_max = (uint32_t)10*i_rist_retry_interval, + .weight = (uint32_t)i_multipeer_mode, + .congestion_control_mode = RIST_CONGESTION_CONTROL_MODE_NORMAL, + .min_retries = 6, + .max_retries = (uint32_t)i_rist_max_retries, + .key_size = i_key_size + }; + + if (psz_shared_secret != NULL && psz_shared_secret[0] != '\0') { + strlcpy(app_peer_config.secret, psz_shared_secret, sizeof(app_peer_config.secret)); + } -static inline ssize_t rist_WriteTo(int fd, const void *buf, size_t len, const struct sockaddr *peer, - socklen_t slen) -{ -#ifdef _WIN32 - # define ENOBUFS WSAENOBUFS - # define EAGAIN WSAEWOULDBLOCK - # define EWOULDBLOCK WSAEWOULDBLOCK -#endif - ssize_t r = -1; - if (slen == 0) - r = send( fd, buf, len, 0 ); - else - r = sendto( fd, buf, len, 0, peer, slen ); - if( r == -1 - && net_errno != EAGAIN && net_errno != EWOULDBLOCK - && net_errno != ENOBUFS && net_errno != ENOMEM ) - { - int type; - if (!getsockopt( fd, SOL_SOCKET, SO_TYPE, - &type, &(socklen_t){ sizeof(type) })) + if ( psz_stream_name != NULL && psz_stream_name[0] != '\0') { + strlcpy(app_peer_config.cname, psz_stream_name, sizeof(app_peer_config.cname)); + } + + // URL overrides (also cleans up the URL) + struct rist_peer_config *peer_config = &app_peer_config; + if (rist_parse_address2(addr[i], &peer_config)) { - if( type == SOCK_DGRAM ) - { - /* ICMP soft error: ignore and retry */ - if (slen == 0) - r = send( fd, buf, len, 0 ); - else - r = sendto( fd, buf, len, 0, peer, slen ); - } + msg_Err( p_this, "Could not parse peer options for sender #%d\n", (int)(i + 1)); + free(addr[i]); + continue; } + + struct rist_peer *peer; + if (rist_peer_create(ctx, &peer, peer_config)) { + msg_Err( p_this, "Could not init rist sender #%i at %s",(int)(i + 1), addr[i] ); + free(addr[i]); + continue; + } + else { + b_peer_alive = true; + } + free(addr[i]); } - return r; -} -static inline ssize_t rist_Write(int fd, const void *buf, size_t len) -{ - return rist_WriteTo(fd, buf, len, NULL, 0); -} + free(psz_shared_secret); + free(psz_stream_name); + free(psz_srp_username); + free(psz_srp_password); -static bool is_multicast_address(char *psz_dst_server) -{ - int ret; - int ismulticast = 0; - - struct addrinfo hint = { - .ai_socktype = SOCK_DGRAM, - .ai_protocol = IPPROTO_UDP, - .ai_flags = AI_NUMERICSERV | AI_IDN | AI_PASSIVE, - }, *res; - - ret = vlc_getaddrinfo(psz_dst_server, 0, &hint, &res); - if (ret) { - return 0; - } else if(res->ai_family == AF_INET) { - unsigned long addr = ntohl(inet_addr(psz_dst_server)); - ismulticast = IN_MULTICAST(addr); - } else if (res->ai_family == AF_INET6) { - if (strlen(psz_dst_server) >= 5 && (strncmp("[ff00", psz_dst_server, 5) == 0 || - strncmp("[FF00", psz_dst_server, 5) == 0)) - ismulticast = 1; - } + return b_peer_alive; - freeaddrinfo(res); +} + +#endif - return ismulticast; -} \ No newline at end of file ===================================== modules/access_output/Makefile.am ===================================== @@ -41,9 +41,10 @@ access_out_LTLIBRARIES += $(LTLIBaccess_output_srt) EXTRA_LTLIBRARIES += libaccess_output_srt_plugin.la ### RIST ### -libaccess_output_rist_plugin_la_SOURCES = access_output/rist.c access/rist.h -libaccess_output_rist_plugin_la_CFLAGS = $(AM_CFLAGS) $(BITSTREAM_CFLAGS) -libaccess_output_rist_plugin_la_LIBADD = $(SOCKET_LIBS) -if HAVE_BITSTREAM -access_out_LTLIBRARIES += libaccess_output_rist_plugin.la -endif + +libaccess_output_rist_plugin_la_SOURCES = access_output/rist.c +libaccess_output_rist_plugin_la_CFLAGS = $(AM_CFLAGS) $(RIST_CFLAGS) +libaccess_output_rist_plugin_la_LIBADD = $(RIST_LIBS) $(SOCKET_LIBS) $(LIBPTHREAD) +libaccess_output_rist_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(access_outdir)' +access_out_LTLIBRARIES += $(LTLIBaccess_output_rist) +EXTRA_LTLIBRARIES += libaccess_output_rist_plugin.la ===================================== modules/access_output/rist.c ===================================== @@ -1,11 +1,9 @@ /***************************************************************************** - * * rist.c: RIST (Reliable Internet Stream Transport) output module + * rist.c: RIST (Reliable Internet Stream Transport) output module ***************************************************************************** - * Copyright (C) 2018, DVEO, the Broadcast Division of Computer Modules, Inc. - * Copyright (C) 2018, SipRadius LLC + * Copyright (C) 2021, SipRadius LLC * * Authors: Sergio Ammirata <[email protected]> - * Daniele Lacamera <[email protected]> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by @@ -28,568 +26,75 @@ #include <vlc_common.h> #include <vlc_interrupt.h> -#include <vlc_fs.h> #include <vlc_plugin.h> #include <vlc_sout.h> #include <vlc_block.h> -#include <vlc_network.h> -#include <vlc_queue.h> -#include <vlc_threads.h> #include <vlc_rand.h> -#ifdef HAVE_POLL_H -#include <poll.h> -#endif #include <sys/time.h> -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif -#include <bitstream/ietf/rtcp_rr.h> -#include <bitstream/ietf/rtcp_sr.h> -#include <bitstream/ietf/rtcp_fb.h> -#include <bitstream/ietf/rtcp_sdes.h> -#include <bitstream/ietf/rtp.h> +#define RIST_CFG_PREFIX "sout-rist-" #include "../access/rist.h" -/* Uncomment the following to introduce induced packet loss for TESTING purposes only */ -/*#define TEST_PACKET_LOSS*/ - -/* The default target packet size */ -#define RIST_TARGET_PACKET_SIZE 1328 -/* The default caching delay for output data */ -#define DEFAULT_CACHING_DELAY 50 -/* The default buffer size in ms */ -#define DEFAULT_BUFFER_SIZE 0 -/* Calculate and print stats once per second */ -#define STATS_INTERVAL 1000 /*ms*/ - -#define MPEG_II_TRANSPORT_STREAM (0x21) -#define RIST_DEFAULT_PORT 1968 - -#define SOUT_CFG_PREFIX "sout-rist-" - static const char *const ppsz_sout_options[] = { - "packet-size", - "caching", - "buffer-size", - "ssrc", - "stream-name", + RIST_CFG_MAX_PACKET_SIZE, + RIST_URL_PARAM_VIRT_SRC_PORT, + RIST_URL_PARAM_VIRT_DST_PORT, + RIST_CFG_LATENCY, + "multipeer-mode", + RIST_CFG_URL2, + RIST_CFG_URL3, + RIST_CFG_URL4, + RIST_URL_PARAM_BANDWIDTH, + RIST_CFG_RETRY_INTERVAL, + RIST_URL_PARAM_REORDER_BUFFER, + RIST_CFG_MAX_RETRIES, + RIST_URL_PARAM_VERBOSE_LEVEL, + RIST_URL_PARAM_CNAME, + RIST_URL_PARAM_PROFILE, + RIST_URL_PARAM_SECRET, + RIST_URL_PARAM_AES_TYPE, + RIST_URL_PARAM_SRP_USERNAME, + RIST_URL_PARAM_SRP_PASSWORD, NULL }; typedef struct { - struct rist_flow *flow; - uint16_t rtp_counter; - char receiver_name[MAX_CNAME]; - uint64_t last_rtcp_tx; - vlc_thread_t ristthread; - vlc_thread_t senderthread; - size_t i_packet_size; - bool b_mtu_warning; - bool b_ismulticast; - vlc_mutex_t lock; - vlc_mutex_t fd_lock; - block_t *p_pktbuffer; - uint64_t i_ticks_caching; - uint32_t ssrc; - bool dead; - vlc_queue_t queue; - /* stats variables */ - uint64_t i_last_stat; - uint32_t i_retransmit_packets; - uint32_t i_total_packets; + struct rist_ctx *sender_ctx; + int gre_src_port; + int gre_dst_port; + uint32_t i_recovery_buffer; + size_t i_max_packet_size; + struct rist_logging_settings logging_settings; } sout_access_out_sys_t; -static struct rist_flow *rist_init_tx() -{ - struct rist_flow *flow = calloc(1, sizeof(struct rist_flow)); - if (!flow) - return NULL; - - flow->reset = 1; - flow->buffer = calloc(RIST_QUEUE_SIZE, sizeof(struct rtp_pkt)); - if ( unlikely( flow->buffer == NULL ) ) - { - free(flow); - return NULL; - } - flow->fd_out = -1; - flow->fd_rtcp = -1; - flow->fd_rtcp_m = -1; - - return flow; -} - -static struct rist_flow *rist_udp_transmitter(sout_access_out_t *p_access, char *psz_dst_server, - int i_dst_port, bool b_ismulticast) -{ - struct rist_flow *flow; - flow = rist_init_tx(); - if (!flow) - return NULL; - - flow->fd_out = net_ConnectDgram(p_access, psz_dst_server, i_dst_port, -1, IPPROTO_UDP ); - if (flow->fd_out < 0) - { - msg_Err( p_access, "cannot open output socket" ); - goto fail; - } - - if (b_ismulticast) { - flow->fd_rtcp_m = net_OpenDgram(p_access, psz_dst_server, i_dst_port + 1, - NULL, 0, IPPROTO_UDP); - if (flow->fd_rtcp_m < 0) - { - msg_Err( p_access, "cannot open multicast nack socket" ); - goto fail; - } - } - - flow->fd_rtcp = net_ConnectDgram(p_access, psz_dst_server, i_dst_port + 1, -1, IPPROTO_UDP ); - if (flow->fd_rtcp < 0) - { - msg_Err( p_access, "cannot open nack socket" ); - goto fail; - } - - char *psz_streamname = NULL; - psz_streamname = var_InheritString( p_access, SOUT_CFG_PREFIX "stream-name" ); - if ( psz_streamname != NULL && psz_streamname[0] != '\0') - { - int name_length = snprintf(flow->cname, MAX_CNAME, "%s", psz_streamname); - if (name_length >= MAX_CNAME) - flow->cname[MAX_CNAME-1] = 0; - free( psz_streamname ); - } - else - populate_cname(flow->fd_rtcp, flow->cname); - - msg_Info(p_access, "our cname is %s", flow->cname); - - return flow; - -fail: - if (flow->fd_out != -1) - vlc_close(flow->fd_out); - if (flow->fd_rtcp != -1) - vlc_close(flow->fd_rtcp); - if (flow->fd_rtcp_m != -1) - vlc_close(flow->fd_rtcp_m); - free(flow->buffer); - free(flow); - return NULL; -} - -static void rist_retransmit(sout_access_out_t *p_access, struct rist_flow *flow, uint16_t seq) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - struct rtp_pkt *pkt = &(flow->buffer[seq]); - if (pkt->buffer == NULL) - { - msg_Err(p_access, "RIST recovery: missing requested packet %d, buffer not yet full", seq); - return; - } - - /* Mark SSID for retransmission (change the last bit of the ssrc to 1) */ - pkt->buffer->p_buffer[11] |= (1 << 0); -#ifdef TEST_PACKET_LOSS -# warning COMPILED WITH SELF INFLICTED PACKET LOSS - if ((flow->packets_count % 14) == 0) { - return; - } -#endif - uint32_t rtp_age = flow->hi_timestamp - pkt->rtp_ts; - uint64_t age = ts_get_from_rtp(rtp_age)/1000; - if (flow->rtp_latency > 0 && rtp_age > flow->rtp_latency) - { - msg_Err(p_access, " Not Sending Nack #%d, too old (age %"PRId64" ms), current seq is:" \ - " [%d]. Perhaps you should increase the buffer-size ...", seq, age, flow->wi); - } - else - { - msg_Dbg(p_access, " Sending Nack #%d (age %"PRId64" ms), current seq is: [%d]", - seq, age, flow->wi); - p_sys->i_retransmit_packets++; - vlc_mutex_lock( &p_sys->fd_lock ); - if (rist_Write(flow->fd_out, pkt->buffer->p_buffer, pkt->buffer->i_buffer) - != (ssize_t)pkt->buffer->i_buffer) { - msg_Err(p_access, "Error sending retransmitted packet after 2 tries ..."); - } - - vlc_mutex_unlock( &p_sys->fd_lock ); - } -} - -static void process_nack(sout_access_out_t *p_access, uint8_t ptype, uint16_t nrecords, - struct rist_flow *flow, uint8_t *pkt) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - int i,j; - - /*msg_Info(p_access, " Nack (BbRR), %d record(s), Window: [%d:%d-->%d]", nrecords, - flow->ri, flow->wi, flow->wi-flow->ri);*/ - - if (ptype == RTCP_PT_RTPFR) - { - uint8_t pi_ssrc[4]; - rtcp_fb_get_ssrc_media_src(pkt, pi_ssrc); - if (memcmp(pi_ssrc, "RIST", 4) != 0) - { - msg_Info(p_access, " Ignoring Nack with name %s", pi_ssrc); - return; /* Ignore app-type not RIST */ - } - - for (i = 0; i < (nrecords-2); i++) { - uint16_t missing; - uint16_t additional; - uint8_t *rtp_nack_record = (pkt + 12 + i * 4); - missing = rtcp_fb_nack_get_range_start(rtp_nack_record); - additional = rtcp_fb_nack_get_range_extra(rtp_nack_record); - /*msg_Info(p_access, " Nack (Range), %d, current seq is: [%d]", missing, flow->wi);*/ - vlc_mutex_lock( &p_sys->lock ); - rist_retransmit(p_access, flow, missing); - for (j = 0; j < additional; j++) { - rist_retransmit(p_access, flow, missing + j + 1); - } - vlc_mutex_unlock( &p_sys->lock ); - } - } - else if (ptype == RTCP_PT_RTPFB) - { - for (i = 0; i < (nrecords-2); i++) { - uint16_t missing; - uint16_t bitmask; - uint8_t *rtp_nack_record = (pkt + 12 + i * 4); - missing = rtcp_fb_nack_get_packet_id(rtp_nack_record); - bitmask = rtcp_fb_nack_get_bitmask_lost(rtp_nack_record); - /*msg_Info(p_access, " Nack (Bitmask), %d, current seq is: [%d]", missing, flow->wi);*/ - vlc_mutex_lock( &p_sys->lock ); - rist_retransmit(p_access, flow, missing); - for (j = 0; j < 16; j++) { - if ((bitmask & (1 << j)) == (1 << j)) { - rist_retransmit(p_access, flow, missing + j + 1); - } - } - vlc_mutex_unlock( &p_sys->lock ); - } - } - else - { - msg_Err(p_access, " !!! Wrong feedback. Ptype is %02x!=%02x, FMT: %02x", ptype, - RTCP_PT_RTPFR, rtcp_fb_get_fmt(pkt)); - } -} - -static void rist_rtcp_recv(sout_access_out_t *p_access, struct rist_flow *flow, uint8_t *pkt_raw, - size_t len) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - uint8_t *pkt = pkt_raw; - uint8_t ptype; - uint16_t processed_bytes = 0; - uint16_t records; - - while (processed_bytes < len) { - pkt = pkt_raw + processed_bytes; - /* safety checks */ - uint16_t bytes_left = len - processed_bytes + 1; - if ( bytes_left < 4 ) - { - /* we must have at least 4 bytes */ - msg_Err(p_access, "Rist rtcp packet must have at least 4 bytes, we have %d", - bytes_left); - return; - } - else if (!rtp_check_hdr(pkt)) - { - /* check for a valid rtp header */ - msg_Err(p_access, "Malformed feedback packet starting with %02x, ignoring.", pkt[0]); - return; - } - - ptype = rtcp_get_pt(pkt); - records = rtcp_get_length(pkt); - uint16_t bytes = (uint16_t)(4 * (1 + records)); - if (bytes > bytes_left) - { - /* check for a sane number of bytes */ - msg_Err(p_access, "Malformed feedback packet, wrong len %d, expecting %u bytes in the" \ - " packet, got a buffer of %u bytes. ptype = %d", rtcp_get_length(pkt), bytes, - bytes_left, ptype); - return; - } - - switch(ptype) { - case RTCP_PT_RTPFR: - case RTCP_PT_RTPFB: - process_nack(p_access, ptype, records, flow, pkt); - break; - - case RTCP_PT_RR: - /* - if (p_sys->b_ismulticast == false) - process_rr(f, pkt, len); - */ - break; - - case RTCP_PT_SDES: - { - if (p_sys->b_ismulticast == false) - { - int8_t name_length = rtcp_sdes_get_name_length(pkt); - if (name_length > bytes_left) - { - /* check for a sane number of bytes */ - msg_Err(p_access, "Malformed SDES packet, wrong cname len %u, got a " \ - "buffer of %u bytes.", name_length, bytes_left); - return; - } - if (memcmp(pkt + RTCP_SDES_SIZE, p_sys->receiver_name, name_length) != 0) - { - memcpy(p_sys->receiver_name, pkt + RTCP_SDES_SIZE, name_length); - msg_Info(p_access, "Receiver name: %s", p_sys->receiver_name); - } - } - } - break; - - case RTCP_PT_SR: - break; - - default: - msg_Err(p_access, " Unrecognized RTCP packet with PTYPE=%02x!!", ptype); - } - processed_bytes += bytes; - } -} - -static void rist_rtcp_send(sout_access_out_t *p_access) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - struct rist_flow *flow = p_sys->flow; - uint8_t rtcp_buf[RTCP_SR_SIZE + RTCP_SDES_SIZE + MAX_CNAME] = { }; - struct timeval tv; - int r; - uint64_t fractions; - uint16_t namelen = strlen(flow->cname) + 1; - gettimeofday(&tv, NULL); - - /* Populate SR for sender report */ - uint8_t *p_sr = rtcp_buf; - rtp_set_hdr(p_sr); - rtcp_sr_set_pt(p_sr); - rtcp_sr_set_length(p_sr, 6); - rtcp_fb_set_int_ssrc_pkt_sender(p_sr, p_sys->ssrc); - rtcp_sr_set_ntp_time_msw(p_sr, tv.tv_sec + SEVENTY_YEARS_OFFSET); - fractions = (uint64_t)tv.tv_usec; - fractions <<= 32ULL; - fractions /= 1000000ULL; - rtcp_sr_set_ntp_time_lsw(p_sr, (uint32_t)fractions); - rtcp_sr_set_rtp_time(p_sr, rtp_get_ts(vlc_tick_now())); - vlc_mutex_lock( &p_sys->lock ); - rtcp_sr_set_packet_count(p_sr, flow->packets_count); - rtcp_sr_set_octet_count(p_sr, flow->bytes_count); - vlc_mutex_unlock( &p_sys->lock ); - - /* Populate SDES for sender description */ - uint8_t *p_sdes = (rtcp_buf + RTCP_SR_SIZE); - /* we need to make sure it is a multiple of 4, pad if necessary */ - if ((namelen - 2) & 0x3) - namelen = ((((namelen - 2) >> 2) + 1) << 2) + 2; - rtp_set_hdr(p_sdes); - rtp_set_cc(p_sdes, 1); /* Actually it is source count in this case */ - rtcp_sdes_set_pt(p_sdes); - rtcp_set_length(p_sdes, (namelen >> 2) + 2); - rtcp_sdes_set_cname(p_sdes, 1); - rtcp_sdes_set_name_length(p_sdes, strlen(flow->cname)); - p_sdes += RTCP_SDES_SIZE; - strlcpy((char *)p_sdes, flow->cname, namelen); - - /* Send the rtcp message */ - r = send(flow->fd_rtcp, rtcp_buf, RTCP_SR_SIZE + RTCP_SDES_SIZE + namelen, 0); - (void)r; -} - -static void *rist_thread(void *data) -{ - sout_access_out_t *p_access = data; - sout_access_out_sys_t *p_sys = p_access->p_sys; - uint64_t now; - uint8_t pkt[RTP_PKT_SIZE]; - struct pollfd pfd[2]; - int ret; - ssize_t r; - - int poll_sockets = 1; - pfd[0].fd = p_sys->flow->fd_rtcp; - pfd[0].events = POLLIN; - if (p_sys->b_ismulticast) - { - pfd[1].fd = p_sys->flow->fd_rtcp_m; - pfd[1].events = POLLIN; - poll_sockets++; - } - - for (;;) { - ret = poll(pfd, poll_sockets, RTCP_INTERVAL >> 1); - int canc = vlc_savecancel(); - if (ret > 0) - { - if (pfd[0].revents & POLLIN) - { - r = rist_Read(p_sys->flow->fd_rtcp, pkt, RTP_PKT_SIZE); - if (r == RTP_PKT_SIZE) { - msg_Err(p_access, "Rist RTCP messsage is too big (%zd bytes) and was probably " \ - "cut, please keep it under %d bytes", r, RTP_PKT_SIZE); - } - if (unlikely(r == -1)) { - msg_Err(p_access, "socket %d error: %s\n", p_sys->flow->fd_rtcp, - gai_strerror(errno)); - } - else { - rist_rtcp_recv(p_access, p_sys->flow, pkt, r); - } - } - if (p_sys->b_ismulticast && (pfd[1].revents & POLLIN)) - { - r = rist_Read(p_sys->flow->fd_rtcp_m, pkt, RTP_PKT_SIZE); - if (r == RTP_PKT_SIZE) { - msg_Err(p_access, "Rist RTCP messsage is too big (%zd bytes) and was " \ - "probably cut, please keep it under %d bytes", r, RTP_PKT_SIZE); - } - if (unlikely(r == -1)) { - msg_Err(p_access, "mcast socket %d error: %s\n", p_sys->flow->fd_rtcp_m, - gai_strerror(errno)); - } - else { - rist_rtcp_recv(p_access, p_sys->flow, pkt, r); - } - } - } - - /* And, in any case: */ - now = vlc_tick_now(); - if ((now - p_sys->last_rtcp_tx) > VLC_TICK_FROM_MS(RTCP_INTERVAL)) - { - rist_rtcp_send(p_access); - p_sys->last_rtcp_tx = now; - } - vlc_restorecancel (canc); - } - - return NULL; -} - -/**************************************************************************** - * RTP send - ****************************************************************************/ -static void* ThreadSend( void *data ) +static int cb_stats(void *arg, const struct rist_stats *stats_container) { - sout_access_out_t *p_access = data; + sout_access_out_t *p_access = (sout_access_out_t*)arg; sout_access_out_sys_t *p_sys = p_access->p_sys; - vlc_tick_t i_caching = p_sys->i_ticks_caching; - struct rist_flow *flow = p_sys->flow; - block_t *out; - while ((out = vlc_queue_DequeueKillable(&p_sys->queue, - &p_sys->dead)) != NULL) + msg_Dbg(p_access, "[RIST-STATS]: %s", stats_container->stats_json); + + const struct rist_stats_sender_peer *stats_sender_peer = &stats_container->stats.sender_peer; + msg_Dbg(p_access, "[RIST-STATS]: name %s, id %"PRIu32", bitrate %zu, sent %"PRIu64", received %"PRIu64", retransmitted %"PRIu64", Q %.2f, rtt %"PRIu32"ms", + stats_sender_peer->cname, + stats_sender_peer->peer_id, + stats_sender_peer->bandwidth, + stats_sender_peer->sent, + stats_sender_peer->received, + stats_sender_peer->retransmitted, + stats_sender_peer->quality, + stats_sender_peer->rtt + ); + + if (stats_sender_peer->rtt > p_sys->i_recovery_buffer) { - ssize_t len = 0; - uint16_t seq = 0; - uint32_t pkt_ts = 0; - - vlc_tick_wait (out->i_dts + i_caching); - - len = out->i_buffer; - - seq = rtp_get_seqnum(out->p_buffer); - pkt_ts = rtp_get_timestamp(out->p_buffer); - - vlc_mutex_lock( &p_sys->fd_lock ); -#ifdef TEST_PACKET_LOSS -# warning COMPILED WITH SELF INFLICTED PACKET LOSS - if ((seq % 14) == 0) { - /*msg_Err(p_access, "Dropped packet with seq number %d ...", seq);*/ - } - else - { - if (rist_Write(flow->fd_out, out->p_buffer, len) != len) { - msg_Err(p_access, "Error sending data packet after 2 tries ..."); - } - } -#else - if (rist_Write(flow->fd_out, out->p_buffer, len) != len) { - msg_Err(p_access, "Error sending data packet after 2 tries ..."); - } -#endif - vlc_mutex_unlock( &p_sys->fd_lock ); - - /* Insert Into Queue */ - vlc_mutex_lock( &p_sys->lock ); - /* Always replace the existing one with the new one */ - struct rtp_pkt *pkt; - pkt = &(flow->buffer[seq]); - if (pkt->buffer) - { - block_Release(pkt->buffer); - pkt->buffer = NULL; - } - pkt->rtp_ts = pkt_ts; - pkt->buffer = out; - - if (flow->reset == 1) - { - msg_Info(p_access, "Traffic detected"); - /* First packet in the queue */ - flow->reset = 0; - } - flow->wi = seq; - flow->hi_timestamp = pkt_ts; - /* Stats for RTCP feedback packets */ - flow->packets_count++; - flow->bytes_count += len; - flow->last_out = seq; - vlc_mutex_unlock( &p_sys->lock ); - - /* We print out the stats once per second */ - uint64_t now = vlc_tick_now(); - uint64_t interval = (now - p_sys->i_last_stat); - if ( interval > VLC_TICK_FROM_MS(STATS_INTERVAL) ) - { - if (p_sys->i_retransmit_packets > 0) - { - float quality = 100; - if (p_sys->i_total_packets > 0) - quality = (float)100 - (float)100*(float)(p_sys->i_retransmit_packets) - /(float)p_sys->i_total_packets; - msg_Info(p_access, "STATS: Total %u, Retransmitted %u, Link Quality %.2f%%", - p_sys->i_total_packets, p_sys->i_retransmit_packets, quality); - } - p_sys->i_last_stat = now; - p_sys->i_retransmit_packets = 0; - p_sys->i_total_packets = 0; - } - p_sys->i_total_packets++; + msg_Err(p_access, "The RTT between us and the receiver is higher than the configured recovery buffer size, %"PRIu32" > %"PRIu32" ms, you should increase the recovery buffer size", + stats_sender_peer->rtt, p_sys->i_recovery_buffer); } - return NULL; -} -static void SendtoFIFO( sout_access_out_t *p_access, block_t *buffer ) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - uint16_t seq = p_sys->rtp_counter++; - - /* Set fresh rtp header data */ - uint8_t *bufhdr = buffer->p_buffer; - rtp_set_hdr(bufhdr); - rtp_set_type(bufhdr, MPEG_II_TRANSPORT_STREAM); - rtp_set_seqnum(bufhdr, seq); - rtp_set_int_ssrc(bufhdr, p_sys->ssrc); - uint32_t pkt_ts = rtp_get_ts(buffer->i_dts); - rtp_set_timestamp(bufhdr, pkt_ts); - - vlc_queue_Enqueue(&p_sys->queue, block_Duplicate(buffer)); + rist_stats_free(stats_container); + return 0; } static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) @@ -597,56 +102,24 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) sout_access_out_sys_t *p_sys = p_access->p_sys; int i_len = 0; + struct rist_data_block rist_buffer = { 0 }; + rist_buffer.virt_src_port = p_sys->gre_src_port; + rist_buffer.virt_dst_port = p_sys->gre_dst_port; + while( p_buffer ) { block_t *p_next; - int i_block_split = 0; - - if( !p_sys->b_mtu_warning && p_buffer->i_buffer > p_sys->i_packet_size ) - { - msg_Warn( p_access, "Buffer data size (%zu) > configured packet size (%zu), you " \ - "should probably increase the configured packet size", p_buffer->i_buffer, - p_sys->i_packet_size ); - p_sys->b_mtu_warning = true; - } - - /* Temp buffer is already too large, flush */ - if( p_sys->p_pktbuffer->i_buffer + p_buffer->i_buffer > p_sys->i_packet_size ) - { - SendtoFIFO(p_access, p_sys->p_pktbuffer); - p_sys->p_pktbuffer->i_buffer = RTP_HEADER_SIZE; - } i_len += p_buffer->i_buffer; while( p_buffer->i_buffer ) { - - size_t i_write = __MIN( p_buffer->i_buffer, p_sys->i_packet_size ); - - i_block_split++; - - if( p_sys->p_pktbuffer->i_buffer == RTP_HEADER_SIZE ) - { - p_sys->p_pktbuffer->i_dts = p_buffer->i_dts; - } - - memcpy( p_sys->p_pktbuffer->p_buffer + p_sys->p_pktbuffer->i_buffer, - p_buffer->p_buffer, i_write ); - - p_sys->p_pktbuffer->i_buffer += i_write; + size_t i_write = __MIN( p_buffer->i_buffer, p_sys->i_max_packet_size ); + rist_buffer.payload = p_buffer->p_buffer; + rist_buffer.payload_len = p_buffer->i_buffer; + rist_sender_data_write(p_sys->sender_ctx, &rist_buffer); p_buffer->p_buffer += i_write; p_buffer->i_buffer -= i_write; - - /* Flush if we reached the target size for the case of block size < target packet size. - * Also flush when we are in block_split > 1 for the case when the block_size is - * larger than the packet-size because we need to continue the inner loop */ - if( p_sys->p_pktbuffer->i_buffer == p_sys->i_packet_size || i_block_split > 1 ) - { - SendtoFIFO(p_access, p_sys->p_pktbuffer); - p_sys->p_pktbuffer->i_buffer = RTP_HEADER_SIZE; - } - } p_next = p_buffer->p_next; @@ -654,10 +127,6 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) p_buffer = p_next; } - - if ( i_len <= 0 ) { - block_ChainRelease( p_buffer ); - } return i_len; } @@ -681,49 +150,13 @@ static int Control( sout_access_out_t *p_access, int i_query, va_list args ) return i_ret; } -static void Clean( sout_access_out_t *p_access ) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - - if ( p_sys->flow ) - { - if (p_sys->flow->fd_out >= 0) { - net_Close (p_sys->flow->fd_out); - } - if (p_sys->flow->fd_rtcp >= 0) { - net_Close (p_sys->flow->fd_rtcp); - } - if (p_sys->flow->fd_rtcp_m >= 0) { - net_Close (p_sys->flow->fd_rtcp_m); - } - for (int i=0; i<RIST_QUEUE_SIZE; i++) { - struct rtp_pkt *pkt = &(p_sys->flow->buffer[i]); - if (pkt->buffer) - { - block_Release(pkt->buffer); - pkt->buffer = NULL; - } - } - free(p_sys->flow->buffer); - free(p_sys->flow); - } - - if (p_sys->p_pktbuffer) - block_Release(p_sys->p_pktbuffer); -} - static void Close( vlc_object_t * p_this ) { sout_access_out_t *p_access = (sout_access_out_t*)p_this; sout_access_out_sys_t *p_sys = p_access->p_sys; - vlc_cancel(p_sys->ristthread); - vlc_queue_Kill(&p_sys->queue, &p_sys->dead); - - vlc_join(p_sys->ristthread, NULL); - vlc_join(p_sys->senderthread, NULL); - - Clean( p_access ); + rist_destroy(p_sys->sender_ctx); + p_sys->sender_ctx = NULL; } static int Open( vlc_object_t *p_this ) @@ -737,78 +170,65 @@ static int Open( vlc_object_t *p_this ) || var_Create ( p_access, "src-addr", VLC_VAR_STRING ) ) { msg_Err( p_access, "Valid network information is required." ); - return VLC_ENOMEM; + return VLC_EGENERIC; } - config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg ); + config_ChainParse( p_access, RIST_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg ); p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; - int i_dst_port = RIST_DEFAULT_PORT; - char *psz_dst_addr; - char *psz_parser = psz_dst_addr = strdup( p_access->psz_path ); - if( !psz_dst_addr ) - return VLC_ENOMEM; + p_access->p_sys = p_sys; - if ( psz_parser[0] == '[' ) - psz_parser = strchr( psz_parser, ']' ); - psz_parser = strchr( psz_parser ? psz_parser : psz_dst_addr, ':' ); - if ( psz_parser != NULL ) - { - *psz_parser++ = '\0'; - i_dst_port = atoi( psz_parser ); + p_sys->gre_src_port = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_SRC_PORT); + p_sys->gre_dst_port = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_DST_PORT); + if (p_sys->gre_dst_port % 2 != 0) { + msg_Err( p_access, "Virtual destination port must be an even number." ); + return VLC_EGENERIC; } - vlc_mutex_init( &p_sys->lock ); - vlc_mutex_init( &p_sys->fd_lock ); + p_sys->i_max_packet_size = rist_get_max_packet_size((vlc_object_t *)p_access); - msg_Info(p_access, "Connecting RIST output to %s:%d and %s:%d", psz_dst_addr, i_dst_port, - psz_dst_addr, i_dst_port+1); - p_sys->b_ismulticast = is_multicast_address(psz_dst_addr); - struct rist_flow *flow = rist_udp_transmitter(p_access, psz_dst_addr, i_dst_port, - p_sys->b_ismulticast); - free (psz_dst_addr); - if (!flow) - goto failed; + int i_rist_profile = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_PROFILE); + int i_verbose_level = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VERBOSE_LEVEL); - p_sys->flow = flow; - flow->latency = var_InheritInteger(p_access, SOUT_CFG_PREFIX "buffer-size"); - flow->rtp_latency = rtp_get_ts(VLC_TICK_FROM_MS(flow->latency)); - p_sys->ssrc = var_InheritInteger(p_access, SOUT_CFG_PREFIX "ssrc"); - if (p_sys->ssrc == 0) { - vlc_rand_bytes(&p_sys->ssrc, 4); + + //disable global logging: see comment in access/rist.c + struct rist_logging_settings rist_global_logging_settings = LOGGING_SETTINGS_INITIALIZER; + if (rist_logging_set_global(&rist_global_logging_settings) != 0) { + msg_Err(p_access,"Could not set logging\n"); + return VLC_EGENERIC; } - /* Last bit of ssrc must be 0 for normal data and 1 for retries */ - p_sys->ssrc &= ~(1 << 0); - - msg_Info(p_access, "SSRC: 0x%08X", p_sys->ssrc); - p_sys->i_ticks_caching = VLC_TICK_FROM_MS(var_InheritInteger( p_access, - SOUT_CFG_PREFIX "caching")); - p_sys->i_packet_size = var_InheritInteger(p_access, SOUT_CFG_PREFIX "packet-size" ); - p_sys->dead = false; - vlc_queue_Init(&p_sys->queue, offsetof (block_t, p_next)); - p_sys->p_pktbuffer = block_Alloc( p_sys->i_packet_size ); - if( unlikely(p_sys->p_pktbuffer == NULL) ) - goto failed; - p_sys->p_pktbuffer->i_buffer = RTP_HEADER_SIZE; + struct rist_logging_settings *logging_settings = &p_sys->logging_settings; + logging_settings->log_socket = -1; + logging_settings->log_stream = NULL; + logging_settings->log_level = i_verbose_level; + logging_settings->log_cb = log_cb; + logging_settings->log_cb_arg = p_access; - p_access->p_sys = p_sys; + if (rist_sender_create(&p_sys->sender_ctx, i_rist_profile, 0, logging_settings) != 0) { + msg_Err( p_access, "Could not create rist sender context\n"); + return VLC_EGENERIC; + } - if( vlc_clone(&p_sys->senderthread, ThreadSend, p_access, VLC_THREAD_PRIORITY_HIGHEST ) ) - { - msg_Err(p_access, "Failed to create sender thread."); + // Enable stats data + if (rist_stats_callback_set(p_sys->sender_ctx, 1000, cb_stats, (void *)p_access) == -1) { + msg_Err(p_access, "Could not enable stats callback"); goto failed; } - if (vlc_clone(&p_sys->ristthread, rist_thread, p_access, VLC_THREAD_PRIORITY_INPUT)) - { - msg_Err(p_access, "Failed to create worker thread."); - vlc_queue_Kill(&p_sys->queue, &p_sys->dead); - vlc_join(p_sys->senderthread, NULL); + int i_multipeer_mode = var_InheritInteger(p_access, RIST_CFG_PREFIX "multipeer-mode"); + int i_recovery_length = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_CFG_LATENCY); + p_sys->i_recovery_buffer = i_recovery_length; + + if ( !rist_add_peers((vlc_object_t *)p_access, p_sys->sender_ctx, p_access->psz_path, i_multipeer_mode, p_sys->gre_dst_port + 1, i_recovery_length) ) + goto failed; + + if (rist_start(p_sys->sender_ctx) == -1) { + msg_Err( p_access, "Could not start rist sender\n"); goto failed; } @@ -818,31 +238,32 @@ static int Open( vlc_object_t *p_this ) return VLC_SUCCESS; failed: - Clean( p_access ); + rist_destroy(p_sys->sender_ctx); + p_sys->sender_ctx = NULL; return VLC_EGENERIC; } -#define CACHING_TEXT N_("RIST data output caching size (ms)") -#define CACHING_LONGTEXT N_( \ - "Having this cache will guarantee that the packets going out are " \ - "delivered at a spacing determined by the chain timestamps thus ensuring " \ - "a near jitter free output. Be aware that this setting will also add to " \ - "the overall latency of the stream." ) - -#define BUFFER_TEXT N_("RIST retry-buffer queue size (ms)") -#define BUFFER_LONGTEXT N_( \ - "This must match the buffer size (latency) configured on the server side. If you " \ - "are not sure, leave the default of 0 which will set it the maximum " \ - "value and will use about 100MB of RAM" ) - -#define SSRC_TEXT N_("SSRC used in RTP output (default is random, i.e. 0)") -#define SSRC_LONGTEXT N_( \ - "Use this setting to specify a known SSRC for the RTP header. This is only useful " \ - "if your receiver acts on it. When using VLC as receiver, it is not." ) - -#define NAME_TEXT N_("Stream name") -#define NAME_LONGTEXT N_( \ - "This Stream name will be sent to the receiver using the rist RTCP channel" ) +#define SRC_PORT_TEXT N_("Virtual Source Port") +#define SRC_PORT_LONGTEXT N_( \ + "Source port to be used inside the reduced-mode of the main profile" ) + +#define DST_PORT_TEXT N_("Virtual Destination Port") +#define DST_PORT_LONGTEXT N_( \ + "Destination port to be used inside the reduced-mode of the main profile" ) + +/* The default target payload size */ +#define RIST_DEFAULT_TARGET_PAYLOAD_SIZE 1316 + +/* Multipeer mode */ +#define RIST_DEFAULT_MULTIPEER_MODE 0 +#define RIST_MULTIPEER_MODE_TEXT N_("Multipeer mode") +#define RIST_MULTIPEER_MODE_LONGTEXT N_( \ + "This allows you to select between duplicate or load balanced modes when " \ + "sending data to multiple peers (several network paths)" ) +static const int multipeer_mode_type[] = { 0, 5, }; +static const char *const multipeer_mode_type_names[] = { + N_("Duplicate"), N_("Load balanced"), +}; /* Module descriptor */ vlc_module_begin() @@ -852,19 +273,49 @@ vlc_module_begin() set_category( CAT_SOUT ) set_subcategory( SUBCAT_SOUT_ACO ) - add_integer( SOUT_CFG_PREFIX "packet-size", RIST_TARGET_PACKET_SIZE, - N_("RIST target packet size (bytes)"), NULL ) - add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_CACHING_DELAY, - CACHING_TEXT, CACHING_LONGTEXT ) - add_integer( SOUT_CFG_PREFIX "buffer-size", DEFAULT_BUFFER_SIZE, + add_integer( RIST_CFG_PREFIX RIST_CFG_MAX_PACKET_SIZE, RIST_DEFAULT_TARGET_PAYLOAD_SIZE, + N_("RIST target payload size (bytes)"), NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_SRC_PORT, RIST_DEFAULT_VIRT_SRC_PORT, + SRC_PORT_TEXT, SRC_PORT_LONGTEXT ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_DST_PORT, RIST_DEFAULT_VIRT_DST_PORT, + DST_PORT_TEXT, DST_PORT_LONGTEXT ) + add_integer( RIST_CFG_PREFIX "multipeer-mode", RIST_DEFAULT_MULTIPEER_MODE, + RIST_MULTIPEER_MODE_TEXT, RIST_MULTIPEER_MODE_LONGTEXT ) + change_integer_list( multipeer_mode_type, multipeer_mode_type_names ) + add_string( RIST_CFG_PREFIX RIST_CFG_URL2, NULL, RIST_URL2_TEXT, NULL ) + add_string( RIST_CFG_PREFIX RIST_CFG_URL3, NULL, RIST_URL3_TEXT, NULL ) + add_string( RIST_CFG_PREFIX RIST_CFG_URL4, NULL, RIST_URL4_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_BANDWIDTH, RIST_DEFAULT_RECOVERY_MAXBITRATE, + RIST_MAX_BITRATE_TEXT, RIST_MAX_BITRATE_LONGTEXT ) + add_integer( RIST_CFG_PREFIX RIST_CFG_RETRY_INTERVAL, RIST_DEFAULT_RECOVERY_RTT_MIN, + RIST_RETRY_INTERVAL_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_REORDER_BUFFER, RIST_DEFAULT_RECOVERY_REORDER_BUFFER, + RIST_REORDER_BUFFER_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_CFG_MAX_RETRIES, RIST_DEFAULT_MAX_RETRIES, + RIST_MAX_RETRIES_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VERBOSE_LEVEL, RIST_DEFAULT_VERBOSE_LEVEL, + RIST_VERBOSE_LEVEL_TEXT, RIST_VERBOSE_LEVEL_LONGTEXT ) + change_integer_list( verbose_level_type, verbose_level_type_names ) + add_integer( RIST_CFG_PREFIX RIST_CFG_LATENCY, RIST_DEFAULT_RECOVERY_LENGHT_MIN, BUFFER_TEXT, BUFFER_LONGTEXT ) - add_integer( SOUT_CFG_PREFIX "ssrc", 0, - SSRC_TEXT, SSRC_LONGTEXT ) - add_string( SOUT_CFG_PREFIX "stream-name", NULL, NAME_TEXT, NAME_LONGTEXT ) - - set_capability( "sout access", 0 ) - add_shortcut( "rist", "tr06" ) + add_string( RIST_CFG_PREFIX RIST_URL_PARAM_CNAME, NULL, RIST_CNAME_TEXT, + RIST_CNAME_LONGTEXT ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_PROFILE, RIST_DEFAULT_PROFILE, + RIST_PROFILE_TEXT, RIST_PROFILE_LONGTEXT ) + add_password( RIST_CFG_PREFIX RIST_URL_PARAM_SECRET, "", + RIST_SHARED_SECRET_TEXT, NULL ) + add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_AES_TYPE, 0, + RIST_ENCRYPTION_TYPE_TEXT, NULL ) + change_integer_list( rist_encryption_type, rist_encryption_type_names ) + add_string( RIST_CFG_PREFIX RIST_URL_PARAM_SRP_USERNAME, "", + RIST_SRP_USERNAME_TEXT, NULL ) + add_password( RIST_CFG_PREFIX RIST_URL_PARAM_SRP_PASSWORD, "", + RIST_SRP_PASSWORD_TEXT, NULL ) + + set_capability( "sout access", 10 ) + add_shortcut( "librist", "rist", "tr06" ) set_callbacks( Open, Close ) vlc_module_end () + View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/bd8359f36ffb8ec44e8f393a28cb3a78a7c5368e...1fa33c1886f37ff50709c56c23e0a4d8a8b7b4d6 -- View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/bd8359f36ffb8ec44e8f393a28cb3a78a7c5368e...1fa33c1886f37ff50709c56c23e0a4d8a8b7b4d6 You're receiving this email because of your account on code.videolan.org.
_______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
