1 /*****************************************************************************
2 * rist.c: RIST (Reliable Internet Stream Transport) input module
3 *****************************************************************************
4 * Copyright (C) 2018, DVEO, the Broadcast Division of Computer Modules, Inc.
5 * Copyright (C) 2018-2020, SipRadius LLC
6 *
7 * Authors: Sergio Ammirata <sergio@ammirata.net>
8 * Daniele Lacamera <root@danielinux.net>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_interrupt.h>
31 #include <vlc_plugin.h>
32 #include <vlc_access.h>
33 #include <vlc_threads.h>
34 #include <vlc_network.h>
35 #include <vlc_block.h>
36 #include <vlc_url.h>
37 #ifdef HAVE_POLL
38 #include <poll.h>
39 #endif
40
41 #include "rist.h"
42
43 /* The default latency is 1000 ms */
44 #define RIST_DEFAULT_LATENCY 1000
45 /* The default nack retry interval */
46 #define RIST_DEFAULT_RETRY_INTERVAL 132
47 /* The default packet re-ordering buffer */
48 #define RIST_DEFAULT_REORDER_BUFFER 70
49 /* The default max packet size */
50 #define RIST_MAX_PACKET_SIZE 1472
51 /* The default timeout is 5 ms */
52 #define RIST_DEFAULT_POLL_TIMEOUT 5
53 /* The max retry count for nacks */
54 #define RIST_MAX_RETRIES 10
55 /* The rate at which we process and send nack requests */
56 #define NACK_INTERVAL 5 /*ms*/
57 /* Calculate and print stats once per second */
58 #define STATS_INTERVAL 1000 /*ms*/
59
60 static const int nack_type[] = {
61 0, 1,
62 };
63
64 static const char *const nack_type_names[] = {
65 N_("Range"), N_("Bitmask"),
66 };
67
68 enum NACK_TYPE {
69 NACK_FMT_RANGE = 0,
70 NACK_FMT_BITMASK
71 };
72
73 struct stream_sys_t
74 {
75 struct rist_flow *flow;
76 char sender_name[MAX_CNAME];
77 enum NACK_TYPE nack_type;
78 uint64_t last_data_rx;
79 uint64_t last_nack_tx;
80 vlc_thread_t thread;
81 int i_max_packet_size;
82 int i_poll_timeout;
83 int i_poll_timeout_current;
84 bool b_ismulticast;
85 bool b_sendnacks;
86 bool b_sendblindnacks;
87 bool b_disablenacks;
88 bool b_flag_discontinuity;
89 block_fifo_t *p_fifo;
90 vlc_mutex_t lock;
91 uint64_t last_message;
92 uint64_t last_reset;
93 /* stat variables */
94 uint32_t i_poll_timeout_zero_count;
95 uint32_t i_poll_timeout_nonzero_count;
96 uint64_t i_last_stat;
97 float vbr_ratio;
98 uint16_t vbr_ratio_count;
99 uint32_t i_lost_packets;
100 uint32_t i_nack_packets;
101 uint32_t i_recovered_packets;
102 uint32_t i_reordered_packets;
103 uint32_t i_total_packets;
104 };
105
Control(stream_t * p_access,int i_query,va_list args)106 static int Control(stream_t *p_access, int i_query, va_list args)
107 {
108 switch( i_query )
109 {
110 case STREAM_CAN_SEEK:
111 case STREAM_CAN_FASTSEEK:
112 case STREAM_CAN_PAUSE:
113 case STREAM_CAN_CONTROL_PACE:
114 *va_arg( args, bool * ) = false;
115 break;
116
117 case STREAM_GET_PTS_DELAY:
118 *va_arg( args, int64_t * ) = RIST_TICK_FROM_MS(
119 var_InheritInteger(p_access, "network-caching") );
120 break;
121
122 default:
123 return VLC_EGENERIC;
124 }
125
126 return VLC_SUCCESS;
127 }
128
rist_init_rx(void)129 static struct rist_flow *rist_init_rx(void)
130 {
131 struct rist_flow *flow = calloc(1, sizeof(struct rist_flow));
132 if (!flow)
133 return NULL;
134
135 flow->reset = 1;
136 flow->buffer = calloc(RIST_QUEUE_SIZE, sizeof(struct rtp_pkt));
137
138 if ( unlikely( flow->buffer == NULL ) )
139 {
140 free(flow);
141 return NULL;
142 }
143 flow->fd_in = -1;
144 flow->fd_nack = -1;
145 flow->fd_rtcp_m = -1;
146
147 return flow;
148 }
149
rist_WriteTo_i11e_Locked(vlc_mutex_t lock,int fd,const void * buf,size_t len,const struct sockaddr * peer,socklen_t slen)150 static void rist_WriteTo_i11e_Locked(vlc_mutex_t lock, int fd, const void *buf, size_t len,
151 const struct sockaddr *peer, socklen_t slen)
152 {
153 vlc_mutex_lock( &lock );
154 rist_WriteTo_i11e(fd, buf, len, peer, slen);
155 vlc_mutex_unlock( &lock );
156 }
157
rist_udp_receiver(stream_t * p_access,vlc_url_t * parsed_url,bool b_ismulticast)158 static struct rist_flow *rist_udp_receiver(stream_t *p_access, vlc_url_t *parsed_url, bool b_ismulticast)
159 {
160 stream_sys_t *p_sys = p_access->p_sys;
161 msg_Info( p_access, "Opening Rist Flow Receiver at %s:%d and %s:%d",
162 parsed_url->psz_host, parsed_url->i_port,
163 parsed_url->psz_host, parsed_url->i_port+1);
164
165 p_sys->flow = rist_init_rx();
166 if (!p_sys->flow)
167 return NULL;
168
169 p_sys->flow->fd_in = net_OpenDgram(p_access, parsed_url->psz_host, parsed_url->i_port, NULL,
170 0, IPPROTO_UDP);
171 if (p_sys->flow->fd_in < 0)
172 {
173 msg_Err( p_access, "cannot open input socket" );
174 goto fail;
175 }
176
177 if (b_ismulticast)
178 {
179 p_sys->flow->fd_rtcp_m = net_OpenDgram(p_access, parsed_url->psz_host, parsed_url->i_port + 1,
180 NULL, 0, IPPROTO_UDP);
181 if (p_sys->flow->fd_rtcp_m < 0)
182 {
183 msg_Err( p_access, "cannot open multicast nack socket" );
184 goto fail;
185 }
186 p_sys->flow->fd_nack = net_ConnectDgram(p_access, parsed_url->psz_host,
187 parsed_url->i_port + 1, -1, IPPROTO_UDP );
188 }
189 else
190 {
191 p_sys->flow->fd_nack = net_OpenDgram(p_access, parsed_url->psz_host, parsed_url->i_port + 1,
192 NULL, 0, IPPROTO_UDP);
193 }
194 if (p_sys->flow->fd_nack < 0)
195 {
196 msg_Err( p_access, "cannot open nack socket" );
197 goto fail;
198 }
199
200 populate_cname(p_sys->flow->fd_nack, p_sys->flow->cname);
201 msg_Info(p_access, "our cname is %s", p_sys->flow->cname);
202
203 return p_sys->flow;
204
205 fail:
206 if (p_sys->flow->fd_in != -1)
207 vlc_close(p_sys->flow->fd_in);
208 if (p_sys->flow->fd_nack != -1)
209 vlc_close(p_sys->flow->fd_nack);
210 if (p_sys->flow->fd_rtcp_m != -1)
211 vlc_close(p_sys->flow->fd_rtcp_m);
212 free(p_sys->flow->buffer);
213 free(p_sys->flow);
214 return NULL;
215 }
216
is_index_in_range(struct rist_flow * flow,uint16_t idx)217 static int is_index_in_range(struct rist_flow *flow, uint16_t idx)
218 {
219 if (flow->ri <= flow->wi) {
220 return ((idx > flow->ri) && (idx <= flow->wi));
221 } else {
222 return ((idx > flow->ri) || (idx <= flow->wi));
223 }
224 }
225
send_rtcp_feedback(stream_t * p_access,struct rist_flow * flow)226 static void send_rtcp_feedback(stream_t *p_access, struct rist_flow *flow)
227 {
228 stream_sys_t *p_sys = p_access->p_sys;
229 int namelen = strlen(flow->cname) + 1;
230
231 /* we need to make sure it is a multiple of 4, pad if necessary */
232 if ((namelen - 2) & 0x3)
233 namelen = ((((namelen - 2) >> 2) + 1) << 2) + 2;
234
235 int rtcp_feedback_size = RTCP_EMPTY_RR_SIZE + RIST_RTCP_SDES_SIZE + namelen;
236 uint8_t *buf = malloc(rtcp_feedback_size);
237 if ( unlikely( buf == NULL ) )
238 return;
239
240 /* Populate RR */
241 uint8_t *rr = buf;
242 rist_rtp_set_hdr(rr);
243 rist_rtcp_rr_set_pt(rr);
244 rist_rtcp_set_length(rr, 1);
245 rist_rtcp_fb_set_int_ssrc_pkt_sender(rr, 0);
246
247 /* Populate SDES */
248 uint8_t *p_sdes = (buf + RTCP_EMPTY_RR_SIZE);
249 rist_rtp_set_hdr(p_sdes);
250 rist_rtp_set_cc(p_sdes, 1); /* Actually it is source count in this case */
251 rist_rtcp_sdes_set_pt(p_sdes);
252 rist_rtcp_set_length(p_sdes, (namelen >> 2) + 2);
253 rist_rtcp_sdes_set_cname(p_sdes, 1);
254 rist_rtcp_sdes_set_name_length(p_sdes, strlen(flow->cname));
255 uint8_t *p_sdes_name = (buf + RTCP_EMPTY_RR_SIZE + RIST_RTCP_SDES_SIZE);
256 strlcpy((char *)p_sdes_name, flow->cname, namelen);
257
258 /* Write to Socket */
259 rist_WriteTo_i11e_Locked(p_sys->lock, flow->fd_nack, buf, rtcp_feedback_size,
260 (struct sockaddr *)&flow->peer_sockaddr, flow->peer_socklen);
261 free(buf);
262 buf = NULL;
263 }
264
send_bbnack(stream_t * p_access,int fd_nack,block_t * pkt_nacks,uint16_t nack_count)265 static void send_bbnack(stream_t *p_access, int fd_nack, block_t *pkt_nacks, uint16_t nack_count)
266 {
267 stream_sys_t *p_sys = p_access->p_sys;
268 struct rist_flow *flow = p_sys->flow;
269 int len = 0;
270
271 int bbnack_bufsize = RIST_RTCP_FB_HEADER_SIZE +
272 RIST_RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count;
273 uint8_t *buf = malloc(bbnack_bufsize);
274 if ( unlikely( buf == NULL ) )
275 return;
276
277 /* Populate NACKS */
278 uint8_t *nack = buf;
279 rist_rtp_set_hdr(nack);
280 rist_rtcp_fb_set_fmt(nack, NACK_FMT_BITMASK);
281 rist_rtcp_set_pt(nack, RIST_RTCP_PT_RTPFB);
282 rist_rtcp_set_length(nack, 2 + nack_count);
283 /*uint8_t name[4] = "RIST";*/
284 /*rist_rtcp_fb_set_ssrc_media_src(nack, name);*/
285 len += RIST_RTCP_FB_HEADER_SIZE;
286 /* TODO : group together */
287 uint16_t nacks[MAX_NACKS];
288 memcpy(nacks, pkt_nacks->p_buffer, pkt_nacks->i_buffer);
289 for (int i = 0; i < nack_count; i++) {
290 uint8_t *nack_record = buf + len + RIST_RTCP_FB_FCI_GENERIC_NACK_SIZE*i;
291 rist_rtcp_fb_nack_set_packet_id(nack_record, nacks[i]);
292 rist_rtcp_fb_nack_set_bitmask_lost(nack_record, 0);
293 }
294 len += RIST_RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count;
295
296 /* Write to Socket */
297 if (p_sys->b_sendnacks && p_sys->b_disablenacks == false)
298 rist_WriteTo_i11e_Locked(p_sys->lock, fd_nack, buf, len,
299 (struct sockaddr *)&flow->peer_sockaddr, flow->peer_socklen);
300 free(buf);
301 buf = NULL;
302 }
303
send_rbnack(stream_t * p_access,int fd_nack,block_t * pkt_nacks,uint16_t nack_count)304 static void send_rbnack(stream_t *p_access, int fd_nack, block_t *pkt_nacks, uint16_t nack_count)
305 {
306 stream_sys_t *p_sys = p_access->p_sys;
307 struct rist_flow *flow = p_sys->flow;
308 int len = 0;
309
310 int rbnack_bufsize = RIST_RTCP_FB_HEADER_SIZE +
311 RIST_RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count;
312 uint8_t *buf = malloc(rbnack_bufsize);
313 if ( unlikely( buf == NULL ) )
314 return;
315
316 /* Populate NACKS */
317 uint8_t *nack = buf;
318 rist_rtp_set_hdr(nack);
319 rist_rtcp_fb_set_fmt(nack, NACK_FMT_RANGE);
320 rist_rtcp_set_pt(nack, RTCP_PT_RTPFR);
321 rist_rtcp_set_length(nack, 2 + nack_count);
322 uint8_t name[4] = "RIST";
323 rist_rtcp_fb_set_ssrc_media_src(nack, name);
324 len += RIST_RTCP_FB_HEADER_SIZE;
325 /* TODO : group together */
326 uint16_t nacks[MAX_NACKS];
327 memcpy(nacks, pkt_nacks->p_buffer, pkt_nacks->i_buffer);
328 for (int i = 0; i < nack_count; i++)
329 {
330 uint8_t *nack_record = buf + len + RIST_RTCP_FB_FCI_GENERIC_NACK_SIZE*i;
331 rtcp_fb_nack_set_range_start(nack_record, nacks[i]);
332 rtcp_fb_nack_set_range_extra(nack_record, 0);
333 }
334 len += RIST_RTCP_FB_FCI_GENERIC_NACK_SIZE * nack_count;
335
336 /* Write to Socket */
337 if (p_sys->b_sendnacks && p_sys->b_disablenacks == false)
338 rist_WriteTo_i11e_Locked(p_sys->lock, fd_nack, buf, len,
339 (struct sockaddr *)&flow->peer_sockaddr, flow->peer_socklen);
340 free(buf);
341 buf = NULL;
342 }
343
send_nacks(stream_t * p_access,struct rist_flow * flow)344 static void send_nacks(stream_t *p_access, struct rist_flow *flow)
345 {
346 stream_sys_t *p_sys = p_access->p_sys;
347 struct rtp_pkt *pkt;
348 uint16_t idx;
349 uint64_t last_ts = 0;
350 uint16_t null_count = 0;
351 int nacks_len = 0;
352 uint16_t nacks[MAX_NACKS];
353
354 idx = flow->ri;
355 while(idx++ != flow->wi)
356 {
357 pkt = &(flow->buffer[idx]);
358 if (pkt->buffer == NULL)
359 {
360 if (nacks_len + 1 >= MAX_NACKS)
361 {
362 break;
363 }
364 else
365 {
366 null_count++;
367 /* TODO: after adding average spacing calculation, change this formula
368 to extrapolated_ts = last_ts + null_count * avg_delta_ts; */
369 uint64_t extrapolated_ts = last_ts;
370 /* Find out the age and add it only if necessary */
371 int retry_count = flow->nacks_retries[idx];
372 uint64_t age = flow->hi_timestamp - extrapolated_ts;
373 uint64_t expiration;
374 if (retry_count == 0){
375 expiration = flow->reorder_buffer;
376 } else {
377 expiration = (uint64_t)flow->nacks_retries[idx] * (uint64_t)flow->retry_interval;
378 }
379 if (age > expiration && retry_count <= flow->max_retries)
380 {
381 flow->nacks_retries[idx]++;
382 nacks[nacks_len++] = idx;
383 msg_Dbg(p_access, "Sending NACK for seq %d, age %"PRId64" ms, retry %u, " \
384 "expiration %"PRId64" ms", idx, ts_get_from_rtp(age)/1000,
385 flow->nacks_retries[idx], ts_get_from_rtp(expiration)/1000);
386 }
387 }
388 }
389 else
390 {
391 last_ts = pkt->rtp_ts;
392 null_count = 0;
393 }
394 }
395 if (nacks_len > 0)
396 {
397 p_sys->i_nack_packets += nacks_len;
398 block_t *pkt_nacks = block_Alloc(nacks_len * 2);
399 if (pkt_nacks)
400 {
401 memcpy(pkt_nacks->p_buffer, nacks, nacks_len * 2);
402 pkt_nacks->i_buffer = nacks_len * 2;
403 block_FifoPut( p_sys->p_fifo, pkt_nacks );
404 }
405 }
406 }
407
sockaddr_cmp(struct sockaddr * x,struct sockaddr * y)408 static int sockaddr_cmp(struct sockaddr *x, struct sockaddr *y)
409 {
410 #define CMP(a, b) if (a != b) return a < b ? -1 : 1
411
412 CMP(x->sa_family, y->sa_family);
413
414 if (x->sa_family == AF_INET)
415 {
416 struct sockaddr_in *xin = (void*)x, *yin = (void*)y;
417 CMP(ntohl(xin->sin_addr.s_addr), ntohl(yin->sin_addr.s_addr));
418 CMP(ntohs(xin->sin_port), ntohs(yin->sin_port));
419 }
420 else if (x->sa_family == AF_INET6)
421 {
422 struct sockaddr_in6 *xin6 = (void*)x, *yin6 = (void*)y;
423 int r = memcmp(xin6->sin6_addr.s6_addr, yin6->sin6_addr.s6_addr,
424 sizeof(xin6->sin6_addr.s6_addr));
425 if (r != 0)
426 return r;
427 CMP(ntohs(xin6->sin6_port), ntohs(yin6->sin6_port));
428 CMP(xin6->sin6_flowinfo, yin6->sin6_flowinfo);
429 CMP(xin6->sin6_scope_id, yin6->sin6_scope_id);
430 }
431
432 #undef CMP
433 return 0;
434 }
435
print_sockaddr_info_change(stream_t * p_access,struct sockaddr * x,struct sockaddr * y)436 static void print_sockaddr_info_change(stream_t *p_access, struct sockaddr *x, struct sockaddr *y)
437 {
438 if (x->sa_family == AF_INET)
439 {
440 struct sockaddr_in *xin = (void*)x, *yin = (void*)y;
441 msg_Info(p_access, "Peer IP:Port change detected: old IP:Port %s:%d, new IP:Port %s:%d",
442 inet_ntoa(xin->sin_addr), ntohs(xin->sin_port), inet_ntoa(yin->sin_addr),
443 ntohs(yin->sin_port));
444 }
445 else if (x->sa_family == AF_INET6)
446 {
447 struct sockaddr_in6 *xin6 = (void*)x, *yin6 = (void*)y;
448 char oldstr[INET6_ADDRSTRLEN];
449 char newstr[INET6_ADDRSTRLEN];
450 inet_ntop(xin6->sin6_family, &xin6->sin6_addr, oldstr, sizeof(struct in6_addr));
451 inet_ntop(yin6->sin6_family, &yin6->sin6_addr, newstr, sizeof(struct in6_addr));
452 msg_Info(p_access, "Peer IP:Port change detected: old IP:Port %s:%d, new IP:Port %s:%d",
453 oldstr, ntohs(xin6->sin6_port), newstr, ntohs(yin6->sin6_port));
454 }
455 }
456
print_sockaddr_info(stream_t * p_access,struct sockaddr * x)457 static void print_sockaddr_info(stream_t *p_access, struct sockaddr *x)
458 {
459 if (x->sa_family == AF_INET)
460 {
461 struct sockaddr_in *xin = (void*)x;
462 msg_Info(p_access, "Peer IP:Port %s:%d", inet_ntoa(xin->sin_addr), ntohs(xin->sin_port));
463 }
464 else if (x->sa_family == AF_INET6)
465 {
466 struct sockaddr_in6 *xin6 = (void*)x;
467 char str[INET6_ADDRSTRLEN];
468 inet_ntop(xin6->sin6_family, &xin6->sin6_addr, str, sizeof(struct in6_addr));
469 msg_Info(p_access, "Peer IP:Port %s:%d", str, ntohs(xin6->sin6_port));
470 }
471 }
472
rtcp_input(stream_t * p_access,struct rist_flow * flow,uint8_t * buf_in,size_t len,struct sockaddr * peer,socklen_t slen)473 static void rtcp_input(stream_t *p_access, struct rist_flow *flow, uint8_t *buf_in, size_t len,
474 struct sockaddr *peer, socklen_t slen)
475 {
476 stream_sys_t *p_sys = p_access->p_sys;
477 uint8_t ptype;
478 uint16_t processed_bytes = 0;
479 uint16_t records;
480 char new_sender_name[MAX_CNAME];
481 uint8_t *buf;
482
483 while (processed_bytes < len) {
484 buf = buf_in + processed_bytes;
485 /* safety checks */
486 uint16_t bytes_left = len - processed_bytes + 1;
487 if ( bytes_left < 4 )
488 {
489 /* we must have at least 4 bytes */
490 msg_Err(p_access, "Rist rtcp packet must have at least 4 bytes, we have %d",
491 bytes_left);
492 return;
493 }
494 else if (!rist_rtp_check_hdr(buf))
495 {
496 /* check for a valid rtp header */
497 msg_Err(p_access, "Malformed rtcp packet starting with %02x, ignoring.", buf[0]);
498 return;
499 }
500
501 ptype = rist_rtcp_get_pt(buf);
502 records = rist_rtcp_get_length(buf);
503 uint16_t bytes = (uint16_t)(4 * (1 + records));
504 if (bytes > bytes_left)
505 {
506 /* check for a sane number of bytes */
507 msg_Err(p_access, "Malformed rtcp packet, wrong len %d, expecting %u bytes in the " \
508 "packet, got a buffer of %u bytes.", rist_rtcp_get_length(buf), bytes, bytes_left);
509 return;
510 }
511
512 switch(ptype) {
513 case RTCP_PT_RTPFR:
514 case RIST_RTCP_PT_RTPFB:
515 break;
516
517 case RIST_RTCP_PT_RR:
518 break;
519
520 case RIST_RTCP_PT_SDES:
521 {
522 if (p_sys->b_sendnacks == false)
523 p_sys->b_sendnacks = true;
524 if (p_sys->b_ismulticast)
525 return;
526 /* Check for changes in source IP address or port */
527 int8_t name_length = rist_rtcp_sdes_get_name_length(buf);
528 if (name_length > bytes_left || name_length <= 0 ||
529 (size_t)name_length > sizeof(new_sender_name))
530 {
531 /* check for a sane number of bytes */
532 msg_Err(p_access, "Malformed SDES packet, wrong cname len %d, got a " \
533 "buffer of %u bytes.", name_length, bytes_left);
534 return;
535 }
536 bool ip_port_changed = false;
537 if (sockaddr_cmp((struct sockaddr *)&flow->peer_sockaddr, peer) != 0)
538 {
539 ip_port_changed = true;
540 if(flow->peer_socklen > 0)
541 print_sockaddr_info_change(p_access,
542 (struct sockaddr *)&flow->peer_sockaddr, peer);
543 else
544 print_sockaddr_info(p_access, peer);
545 vlc_mutex_lock( &p_sys->lock );
546 memcpy(&flow->peer_sockaddr, peer, sizeof(struct sockaddr_storage));
547 flow->peer_socklen = slen;
548 vlc_mutex_unlock( &p_sys->lock );
549 }
550
551 /* Check for changes in cname */
552 bool peer_name_changed = false;
553 memset(new_sender_name, 0, MAX_CNAME);
554 memcpy(new_sender_name, buf + RIST_RTCP_SDES_SIZE, name_length);
555 if (memcmp(new_sender_name, p_sys->sender_name, name_length) != 0)
556 {
557 peer_name_changed = true;
558 if (strcmp(p_sys->sender_name, "") == 0)
559 msg_Info(p_access, "Peer Name: %s", new_sender_name);
560 else
561 msg_Info(p_access, "Peer Name change detected: old Name: %s, new " \
562 "Name: %s", p_sys->sender_name, new_sender_name);
563 memset(p_sys->sender_name, 0, MAX_CNAME);
564 memcpy(p_sys->sender_name, buf + RIST_RTCP_SDES_SIZE, name_length);
565 }
566
567 /* Reset the buffer as the source must have been restarted */
568 if (peer_name_changed || ip_port_changed)
569 {
570 /* reset the buffer */
571 flow->reset = 1;
572 }
573 }
574 break;
575
576 case RIST_RTCP_PT_SR:
577 if (p_sys->b_sendnacks == false)
578 p_sys->b_sendnacks = true;
579 if (p_sys->b_ismulticast)
580 return;
581 break;
582
583 default:
584 msg_Err(p_access, " Unrecognized RTCP packet with PTYPE=%02x!!", ptype);
585 }
586 processed_bytes += (4 * (1 + records));
587 }
588 }
589
rist_input(stream_t * p_access,struct rist_flow * flow,uint8_t * buf,size_t len)590 static bool rist_input(stream_t *p_access, struct rist_flow *flow, uint8_t *buf, size_t len)
591 {
592 stream_sys_t *p_sys = p_access->p_sys;
593
594 /* safety checks */
595 if ( len < RIST_RTP_HEADER_SIZE )
596 {
597 /* check if packet size >= rtp header size */
598 msg_Err(p_access, "Rist rtp packet must have at least 12 bytes, we have %zu", len);
599 return false;
600 }
601 else if (!rist_rtp_check_hdr(buf))
602 {
603 /* check for a valid rtp header */
604 msg_Err(p_access, "Malformed rtp packet header starting with %02x, ignoring.", buf[0]);
605 return false;
606 }
607
608 uint16_t idx = rist_rtp_get_seqnum(buf);
609 uint32_t pkt_ts = rist_rtp_get_timestamp(buf);
610 bool retrasnmitted = false;
611 bool success = true;
612
613 if (flow->reset == 1)
614 {
615 msg_Info(p_access, "Traffic detected after buffer reset");
616 /* First packet in the queue */
617 flow->hi_timestamp = pkt_ts;
618 msg_Info(p_access, "ts@%u", flow->hi_timestamp);
619 flow->wi = idx;
620 flow->ri = idx;
621 flow->reset = 0;
622 p_sys->b_flag_discontinuity = true;
623 }
624
625 /* Check to see if this is a retransmission or a regular packet */
626 if (buf[11] & (1 << 0))
627 {
628 msg_Dbg(p_access, "Packet %d RECOVERED, Window: [%d:%d-->%d]", idx, flow->ri, flow->wi,
629 flow->wi-flow->ri);
630 p_sys->i_recovered_packets++;
631 retrasnmitted = true;
632 }
633 else if (flow->wi != flow->ri)
634 {
635 /* Reset counter to 0 on incoming holes */
636 /* Regular packets only as retransmits are expected to come in out of order */
637 uint16_t idxnext = (uint16_t)(flow->wi + 1);
638 if (idx != idxnext)
639 {
640 if (idx > idxnext) {
641 msg_Dbg(p_access, "Gap, got %d, expected %d, %d packet gap, Window: [%d:%d-->%d]",
642 idx, idxnext, idx - idxnext, flow->ri, flow->wi, (uint16_t)(flow->wi-flow->ri));
643 } else {
644 p_sys->i_reordered_packets++;
645 msg_Dbg(p_access, "Out of order, got %d, expected %d, Window: [%d:%d-->%d]", idx,
646 idxnext, flow->ri, flow->wi, (uint16_t)(flow->wi-flow->ri));
647 }
648 uint16_t zero_counter = (uint16_t)(flow->wi + 1);
649 while(zero_counter++ != idx) {
650 flow->nacks_retries[zero_counter] = 0;
651 }
652 /*msg_Dbg(p_access, "Gap, reseting %d packets as zero nacks %d to %d",
653 idx - flow->wi - 1, (uint16_t)(flow->wi + 1), idx);*/
654 }
655 }
656
657 /* Always replace the existing one with the new one */
658 struct rtp_pkt *pkt;
659 pkt = &(flow->buffer[idx]);
660 if (pkt->buffer && pkt->buffer->i_buffer > 0)
661 {
662 block_Release(pkt->buffer);
663 pkt->buffer = NULL;
664 }
665 pkt->buffer = block_Alloc(len);
666 if (!pkt->buffer)
667 return false;
668
669 pkt->buffer->i_buffer = len;
670 memcpy(pkt->buffer->p_buffer, buf, len);
671 pkt->rtp_ts = pkt_ts;
672 p_sys->last_data_rx = mdate();
673 /* Reset the try counter regardless of wether it was a retransmit or not */
674 flow->nacks_retries[idx] = 0;
675
676 if (retrasnmitted)
677 return success;
678
679 p_sys->i_total_packets++;
680 /* Perform discontinuity checks and udpdate counters */
681 if (!is_index_in_range(flow, idx) && pkt_ts >= flow->hi_timestamp)
682 {
683 if ((pkt_ts - flow->hi_timestamp) > flow->hi_timestamp/10)
684 {
685 msg_Info(p_access, "Forward stream discontinuity idx@%d/%d/%d ts@%u/%u", flow->ri, idx,
686 flow->wi, pkt_ts, flow->hi_timestamp);
687 flow->reset = 1;
688 success = false;
689 }
690 else
691 {
692 flow->wi = idx;
693 flow->hi_timestamp = pkt_ts;
694 }
695 }
696 else if (!is_index_in_range(flow, idx))
697 {
698 /* incoming timestamp just jumped back in time or index is outside of scope */
699 msg_Info(p_access, "Backwards stream discontinuity idx@%d/%d/%d ts@%u/%u", flow->ri, idx,
700 flow->wi, pkt_ts, flow->hi_timestamp);
701 flow->reset = 1;
702 success = false;
703 }
704
705 return success;
706 }
707
rist_dequeue(stream_t * p_access,struct rist_flow * flow)708 static block_t *rist_dequeue(stream_t *p_access, struct rist_flow *flow)
709 {
710 stream_sys_t *p_sys = p_access->p_sys;
711 block_t *pktout = NULL;
712 struct rtp_pkt *pkt;
713 uint16_t idx;
714 if (flow->ri == flow->wi || flow->reset > 0)
715 return NULL;
716
717 idx = flow->ri;
718 bool found_data = false;
719 uint16_t loss_amount = 0;
720 while(idx++ != flow->wi) {
721
722 pkt = &(flow->buffer[idx]);
723 if (!pkt->buffer)
724 {
725 /*msg_Info(p_access, "Possible packet loss on index #%d", idx);*/
726 loss_amount++;
727 /* We move ahead until we find a timestamp but we do not move the cursor.
728 * None of them are guaranteed packet loss because we do not really
729 * know their timestamps. They might still arrive on the next loop.
730 * We can confirm the loss only if we get a valid packet in the loop below. */
731 continue;
732 }
733
734 /*printf("IDX=%d, flow->hi_timestamp: %u, (ts + flow->rtp_latency): %u\n", idx,
735 flow->hi_timestamp, (ts - 100 * flow->qdelay));*/
736 if (flow->hi_timestamp > (uint32_t)(pkt->rtp_ts + flow->rtp_latency))
737 {
738 /* Populate output packet now but remove rtp header from source */
739 int newSize = pkt->buffer->i_buffer - RIST_RTP_HEADER_SIZE;
740 pktout = block_Alloc(newSize);
741 if (pktout)
742 {
743 pktout->i_buffer = newSize;
744 memcpy(pktout->p_buffer, pkt->buffer->p_buffer + RIST_RTP_HEADER_SIZE, newSize);
745 /* free the buffer and increase the read index */
746 flow->ri = idx;
747 /* TODO: calculate average duration using buffer average (bring from sender) */
748 found_data = true;
749 }
750 block_Release(pkt->buffer);
751 pkt->buffer = NULL;
752 break;
753 }
754
755 }
756
757 if (loss_amount > 0 && found_data == true)
758 {
759 /* Packet loss confirmed, we found valid data after the holes */
760 msg_Dbg(p_access, "Packet NOT RECOVERED, %d packet(s), Window: [%d:%d]", loss_amount,
761 flow->ri, flow->wi);
762 p_sys->i_lost_packets += loss_amount;
763 p_sys->b_flag_discontinuity = true;
764 }
765
766 return pktout;
767 }
768
rist_thread(void * data)769 static void *rist_thread(void *data)
770 {
771 stream_t *p_access = data;
772 stream_sys_t *p_sys = p_access->p_sys;
773
774 /* Process nacks every 5ms */
775 /* We only ask for the relevant ones */
776 for (;;) {
777 block_t *pkt_nacks = block_FifoGet(p_sys->p_fifo);
778
779 int canc = vlc_savecancel();
780
781 /* there are two bytes per nack */
782 uint16_t nack_count = (uint16_t)pkt_nacks->i_buffer/2;
783 switch(p_sys->nack_type) {
784 case NACK_FMT_BITMASK:
785 send_bbnack(p_access, p_sys->flow->fd_nack, pkt_nacks, nack_count);
786 break;
787
788 default:
789 send_rbnack(p_access, p_sys->flow->fd_nack, pkt_nacks, nack_count);
790 }
791
792 if (nack_count > 1)
793 msg_Dbg(p_access, "Sent %u NACKs !!!", nack_count);
794 block_Release(pkt_nacks);
795
796 vlc_restorecancel (canc);
797 }
798
799 return NULL;
800 }
801
BlockRIST(stream_t * p_access,bool * restrict eof)802 static block_t *BlockRIST(stream_t *p_access, bool *restrict eof)
803 {
804 stream_sys_t *p_sys = p_access->p_sys;
805 uint64_t now;
806 *eof = false;
807 block_t *pktout = NULL;
808 struct pollfd pfd[3];
809 int ret;
810 ssize_t r;
811 struct sockaddr_storage peer;
812 socklen_t slen = sizeof(struct sockaddr_storage);
813 struct rist_flow *flow = p_sys->flow;
814
815 if (vlc_killed())
816 {
817 *eof = true;
818 return NULL;
819 }
820
821 int poll_sockets = 2;
822 pfd[0].fd = flow->fd_in;
823 pfd[0].events = POLLIN;
824 pfd[1].fd = flow->fd_nack;
825 pfd[1].events = POLLIN;
826 if (p_sys->b_ismulticast)
827 {
828 pfd[2].fd = flow->fd_rtcp_m;
829 pfd[2].events = POLLIN;
830 poll_sockets++;
831 }
832
833 /* The protocol uses a fifo buffer with a fixed time delay.
834 * That buffer needs to be emptied at a rate that is determined by the rtp timestamps of the
835 * packets. If I waited indefinitely for data coming in, the rate and delay of output packets
836 * would be wrong. I am calling the rist_dequeue function every time a data packet comes in
837 * and also every time we get a poll timeout. The configurable poll timeout is for controling
838 * the maximum jitter of output data coming out of the buffer. The default 5ms timeout covers
839 * most cases. */
840
841 ret = vlc_poll_i11e(pfd, poll_sockets, p_sys->i_poll_timeout_current);
842 if (unlikely(ret < 0))
843 return NULL;
844 else if (ret == 0)
845 {
846 /* Poll timeout, check the queue for the next packet that needs to be delivered */
847 pktout = rist_dequeue(p_access, flow);
848 /* if there is data, we need to come back faster to finish emptying it */
849 if (pktout) {
850 p_sys->i_poll_timeout_current = 0;
851 p_sys->i_poll_timeout_zero_count++;
852 } else {
853 p_sys->i_poll_timeout_current = p_sys->i_poll_timeout;
854 p_sys->i_poll_timeout_nonzero_count++;
855 }
856 }
857 else
858 {
859
860 uint8_t *buf = malloc(p_sys->i_max_packet_size);
861 if ( unlikely( buf == NULL ) )
862 return NULL;
863
864 /* Process rctp incoming data */
865 if (pfd[1].revents & POLLIN)
866 {
867 r = rist_ReadFrom_i11e(flow->fd_nack, buf, p_sys->i_max_packet_size,
868 (struct sockaddr *)&peer, &slen);
869 if (unlikely(r == -1)) {
870 msg_Err(p_access, "socket %d error: %s\n", flow->fd_nack, gai_strerror(errno));
871 }
872 else {
873 if (p_sys->b_ismulticast == false)
874 rtcp_input(p_access, flow, buf, r, (struct sockaddr *)&peer, slen);
875 }
876 }
877 if (p_sys->b_ismulticast && pfd[2].revents & POLLIN)
878 {
879 r = rist_ReadFrom_i11e(flow->fd_rtcp_m, buf, p_sys->i_max_packet_size,
880 (struct sockaddr *)&peer, &slen);
881 if (unlikely(r == -1)) {
882 msg_Err(p_access, "mcast socket %d error: %s\n",flow->fd_rtcp_m, gai_strerror(errno));
883 }
884 else {
885 rtcp_input(p_access, flow, buf, r, (struct sockaddr *)&peer, slen);
886 }
887 }
888
889 /* Process regular incoming data */
890 if (pfd[0].revents & POLLIN)
891 {
892 r = rist_Read_i11e(flow->fd_in, buf, p_sys->i_max_packet_size);
893 if (unlikely(r == -1)) {
894 msg_Err(p_access, "socket %d error: %s\n", flow->fd_in, gai_strerror(errno));
895 }
896 else
897 {
898 /* rist_input will process and queue the pkt */
899 if (rist_input(p_access, flow, buf, r))
900 {
901 /* Check the queue for the next packet that needs to be delivered */
902 pktout = rist_dequeue(p_access, flow);
903 if (pktout) {
904 p_sys->i_poll_timeout_current = 0;
905 p_sys->i_poll_timeout_zero_count++;
906 } else {
907 p_sys->i_poll_timeout_current = p_sys->i_poll_timeout;
908 p_sys->i_poll_timeout_nonzero_count++;
909 }
910 }
911 }
912 }
913
914 free(buf);
915 buf = NULL;
916 }
917
918 now = mdate();
919
920 /* Process stats and print them out */
921 /* We need to measure some items every 70ms */
922 uint64_t interval = (now - flow->feedback_time);
923 if ( interval > RIST_TICK_FROM_MS(RTCP_INTERVAL) )
924 {
925 if (p_sys->i_poll_timeout_nonzero_count > 0)
926 {
927 float ratio = (float)p_sys->i_poll_timeout_zero_count
928 / (float)p_sys->i_poll_timeout_nonzero_count;
929 if (ratio <= 1)
930 p_sys->vbr_ratio += 1 - ratio;
931 else
932 p_sys->vbr_ratio += ratio - 1;
933 p_sys->vbr_ratio_count++;
934 /*msg_Dbg(p_access, "zero poll %u, non-zero poll %u, ratio %.2f",
935 p_sys->i_poll_timeout_zero_count, p_sys->i_poll_timeout_nonzero_count, ratio);*/
936 p_sys->i_poll_timeout_zero_count = 0;
937 p_sys->i_poll_timeout_nonzero_count = 0;
938 }
939 }
940 /* We print out the stats once per second */
941 interval = (now - p_sys->i_last_stat);
942 if ( interval > RIST_TICK_FROM_MS(STATS_INTERVAL) )
943 {
944 if ( p_sys->i_lost_packets > 0)
945 msg_Err(p_access, "We have %d lost packets", p_sys->i_lost_packets);
946 float ratio = 1;
947 if (p_sys->vbr_ratio_count > 0)
948 ratio = p_sys->vbr_ratio / (float)p_sys->vbr_ratio_count;
949 float quality = 100;
950 if (p_sys->i_total_packets > 0)
951 quality -= (float)100*(float)(p_sys->i_lost_packets + p_sys->i_recovered_packets +
952 p_sys->i_reordered_packets)/(float)p_sys->i_total_packets;
953 if (quality != 100)
954 msg_Info(p_access, "STATS: Total %u, Recovered %u/%u, Reordered %u, Lost %u, VBR " \
955 "Score %.2f, Link Quality %.2f%%", p_sys->i_total_packets,
956 p_sys->i_recovered_packets, p_sys->i_nack_packets, p_sys->i_reordered_packets,
957 p_sys->i_lost_packets, ratio, quality);
958 p_sys->i_last_stat = now;
959 p_sys->vbr_ratio = 0;
960 p_sys->vbr_ratio_count = 0;
961 p_sys->i_lost_packets = 0;
962 p_sys->i_nack_packets = 0;
963 p_sys->i_recovered_packets = 0;
964 p_sys->i_reordered_packets = 0;
965 p_sys->i_total_packets = 0;
966 }
967
968 /* Send rtcp feedback every RTCP_INTERVAL */
969 interval = (now - flow->feedback_time);
970 if ( interval > RIST_TICK_FROM_MS(RTCP_INTERVAL) )
971 {
972 /* msg_Dbg(p_access, "Calling RTCP Feedback %lu<%d ms using timer", interval,
973 RIST_TICK_FROM_MS(RTCP_INTERVAL)); */
974 send_rtcp_feedback(p_access, flow);
975 flow->feedback_time = now;
976 }
977
978 /* Send nacks every NACK_INTERVAL (only the ones that have matured, if any) */
979 interval = (now - p_sys->last_nack_tx);
980 if ( interval > RIST_TICK_FROM_MS(NACK_INTERVAL) )
981 {
982 send_nacks(p_access, p_sys->flow);
983 p_sys->last_nack_tx = now;
984 }
985
986 /* Safety check for when the input stream stalls */
987 if ( p_sys->last_data_rx > 0 && now > p_sys->last_data_rx &&
988 (uint64_t)(now - p_sys->last_data_rx) > (uint64_t)RIST_TICK_FROM_MS(flow->latency) &&
989 (uint64_t)(now - p_sys->last_reset) > (uint64_t)RIST_TICK_FROM_MS(flow->latency) )
990 {
991 msg_Err(p_access, "No data received for %"PRId64" ms, resetting buffers",
992 (int64_t)(now - p_sys->last_data_rx)/1000);
993 p_sys->last_reset = now;
994 flow->reset = 1;
995 }
996
997 if (pktout)
998 {
999 if (p_sys->b_flag_discontinuity) {
1000 pktout->i_flags |= BLOCK_FLAG_DISCONTINUITY;
1001 p_sys->b_flag_discontinuity = false;
1002 }
1003 return pktout;
1004 }
1005 else
1006 return NULL;
1007 }
1008
Clean(stream_t * p_access)1009 static void Clean( stream_t *p_access )
1010 {
1011 stream_sys_t *p_sys = p_access->p_sys;
1012
1013 if( likely(p_sys->p_fifo != NULL) )
1014 block_FifoRelease( p_sys->p_fifo );
1015
1016 if (p_sys->flow)
1017 {
1018 if (p_sys->flow->fd_in >= 0)
1019 net_Close (p_sys->flow->fd_in);
1020 if (p_sys->flow->fd_nack >= 0)
1021 net_Close (p_sys->flow->fd_nack);
1022 if (p_sys->flow->fd_rtcp_m >= 0)
1023 net_Close (p_sys->flow->fd_rtcp_m);
1024 for (int i=0; i<RIST_QUEUE_SIZE; i++) {
1025 struct rtp_pkt *pkt = &(p_sys->flow->buffer[i]);
1026 if (pkt->buffer && pkt->buffer->i_buffer > 0) {
1027 block_Release(pkt->buffer);
1028 pkt->buffer = NULL;
1029 }
1030 }
1031 free(p_sys->flow->buffer);
1032 free(p_sys->flow);
1033 }
1034 }
1035
Close(vlc_object_t * p_this)1036 static void Close(vlc_object_t *p_this)
1037 {
1038 stream_t *p_access = (stream_t*)p_this;
1039 stream_sys_t *p_sys = p_access->p_sys;
1040
1041 vlc_cancel(p_sys->thread);
1042 vlc_join(p_sys->thread, NULL);
1043
1044 Clean( p_access );
1045 }
1046
Open(vlc_object_t * p_this)1047 static int Open(vlc_object_t *p_this)
1048 {
1049 stream_t *p_access = (stream_t*)p_this;
1050 stream_sys_t *p_sys = NULL;
1051 vlc_url_t parsed_url = { 0 };
1052
1053 p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) );
1054 if( unlikely( p_sys == NULL ) )
1055 return VLC_ENOMEM;
1056
1057 p_access->p_sys = p_sys;
1058
1059 vlc_mutex_init( &p_sys->lock );
1060
1061 if ( vlc_UrlParse( &parsed_url, p_access->psz_url ) == -1 )
1062 {
1063 msg_Err( p_access, "Failed to parse input URL (%s)",
1064 p_access->psz_url );
1065 goto failed;
1066 }
1067
1068 /* Initialize rist flow */
1069 p_sys->b_ismulticast = is_multicast_address(parsed_url.psz_host);
1070 p_sys->flow = rist_udp_receiver(p_access, &parsed_url, p_sys->b_ismulticast);
1071 vlc_UrlClean( &parsed_url );
1072 if (!p_sys->flow)
1073 {
1074 msg_Err( p_access, "Failed to open rist flow (%s)",
1075 p_access->psz_url );
1076 goto failed;
1077 }
1078
1079 p_sys->b_flag_discontinuity = false;
1080 p_sys->b_disablenacks = var_InheritBool( p_access, "disable-nacks" );
1081 p_sys->b_sendblindnacks = var_InheritBool( p_access, "mcast-blind-nacks" );
1082 if (p_sys->b_sendblindnacks && p_sys->b_disablenacks == false)
1083 p_sys->b_sendnacks = true;
1084 else
1085 p_sys->b_sendnacks = false;
1086 p_sys->nack_type = var_InheritInteger( p_access, "nack-type" );
1087 p_sys->i_max_packet_size = var_InheritInteger( p_access, "packet-size" );
1088 p_sys->i_poll_timeout = var_InheritInteger( p_access, "maximum-jitter" );
1089 p_sys->flow->retry_interval = var_InheritInteger( p_access, "retry-interval" );
1090 p_sys->flow->max_retries = var_InheritInteger( p_access, "max-retries" );
1091 p_sys->flow->latency = var_InheritInteger( p_access, "latency" );
1092 if (p_sys->b_disablenacks)
1093 p_sys->flow->reorder_buffer = p_sys->flow->latency;
1094 else
1095 p_sys->flow->reorder_buffer = var_InheritInteger( p_access, "reorder-buffer" );
1096 msg_Info(p_access, "Setting queue latency to %d ms", p_sys->flow->latency);
1097
1098 /* Convert to rtp times */
1099 p_sys->flow->rtp_latency = rtp_get_ts(RIST_TICK_FROM_MS(p_sys->flow->latency));
1100 p_sys->flow->retry_interval = rtp_get_ts(RIST_TICK_FROM_MS(p_sys->flow->retry_interval));
1101 p_sys->flow->reorder_buffer = rtp_get_ts(RIST_TICK_FROM_MS(p_sys->flow->reorder_buffer));
1102
1103 p_sys->p_fifo = block_FifoNew();
1104 if( unlikely(p_sys->p_fifo == NULL) )
1105 goto failed;
1106
1107 /* This extra thread is for sending feedback/nack packets even when no data comes in */
1108 if (vlc_clone(&p_sys->thread, rist_thread, p_access, VLC_THREAD_PRIORITY_INPUT))
1109 {
1110 msg_Err(p_access, "Failed to create worker thread.");
1111 goto failed;
1112 }
1113
1114 p_access->pf_block = BlockRIST;
1115 p_access->pf_control = Control;
1116
1117 return VLC_SUCCESS;
1118
1119 failed:
1120 Clean( p_access );
1121 return VLC_EGENERIC;
1122 }
1123
1124 /* Module descriptor */
1125 vlc_module_begin ()
1126
1127 set_shortname( N_("RIST") )
1128 set_description( N_("RIST input") )
1129 set_category( CAT_INPUT )
1130 set_subcategory( SUBCAT_INPUT_ACCESS )
1131
1132 add_integer( "packet-size", RIST_MAX_PACKET_SIZE,
1133 N_("RIST maximum packet size (bytes)"), NULL, true )
1134 add_integer( "maximum-jitter", RIST_DEFAULT_POLL_TIMEOUT,
1135 N_("RIST demux/decode maximum jitter (default is 5ms)"),
1136 N_("This controls the maximum jitter that will be passed to the demux/decode chain. "
1137 "The lower the value, the more CPU cycles the algorithm will consume"), true )
1138 add_integer( "latency", RIST_DEFAULT_LATENCY, N_("RIST latency (ms)"), NULL, true )
1139 add_integer( "retry-interval", RIST_DEFAULT_RETRY_INTERVAL, N_("RIST nack retry interval (ms)"),
1140 NULL, true )
1141 add_integer( "reorder-buffer", RIST_DEFAULT_REORDER_BUFFER, N_("RIST reorder buffer (ms)"),
1142 NULL, true )
1143 add_integer( "max-retries", RIST_MAX_RETRIES, N_("RIST maximum retry count"), NULL, true )
1144 add_integer( "nack-type", NACK_FMT_RANGE,
1145 N_("RIST nack type, 0 = range, 1 = bitmask. Default is range"), NULL, true )
1146 change_integer_list( nack_type, nack_type_names )
1147 add_bool( "disable-nacks", false, N_("Disable NACK output packets"),
1148 N_("Use this to disable packet recovery"), true )
1149 add_bool( "mcast-blind-nacks", false, N_("Do not check for a valid rtcp message from the encoder"),
1150 N_("Send nack messages even when we have not confirmed that the encoder is on our local " \
1151 "network."), true )
1152
1153 set_capability( "access", 0 )
1154 add_shortcut( "rist", "tr06" )
1155
1156 set_callbacks( Open, Close )
1157
1158 vlc_module_end ()
1159