access_out: rist: close the sockets on error
[vlc.git] / modules / access_output / rist.c
blobd46f170d39232ed1f8118d698065555993fecdb1
1 /*****************************************************************************
2 * * rist.c: RIST (Reliable Internet Stream Transport) output module
3 *****************************************************************************
4 * Copyright (C) 2018, DVEO, the Broadcast Division of Computer Modules, Inc.
5 * Copyright (C) 2018, SipRadius LLC
7 * Authors: Sergio Ammirata <sergio@ammirata.net>
8 * Daniele Lacamera <root@danielinux.net>
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.
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.
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 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <vlc_common.h>
30 #include <vlc_interrupt.h>
31 #include <vlc_fs.h>
32 #include <vlc_plugin.h>
33 #include <vlc_sout.h>
34 #include <vlc_block.h>
35 #include <vlc_network.h>
36 #include <vlc_threads.h>
37 #include <vlc_rand.h>
38 #ifdef HAVE_POLL
39 #include <poll.h>
40 #endif
41 #include <sys/time.h>
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif
45 #include <bitstream/ietf/rtcp_rr.h>
46 #include <bitstream/ietf/rtcp_sr.h>
47 #include <bitstream/ietf/rtcp_fb.h>
48 #include <bitstream/ietf/rtcp_sdes.h>
49 #include <bitstream/ietf/rtp.h>
51 #include "../access/rist.h"
53 /* Uncomment the following to introduce induced packet loss for TESTING purposes only */
54 /*#define TEST_PACKET_LOSS*/
56 /* The default target packet size */
57 #define RIST_TARGET_PACKET_SIZE 1328
58 /* The default caching delay for output data */
59 #define DEFAULT_CACHING_DELAY 50
60 /* The default buffer size in ms */
61 #define DEFAULT_BUFFER_SIZE 0
62 /* Calculate and print stats once per second */
63 #define STATS_INTERVAL 1000 /*ms*/
65 #define MPEG_II_TRANSPORT_STREAM (0x21)
66 #define RIST_DEFAULT_PORT 1968
68 #define SOUT_CFG_PREFIX "sout-rist-"
70 static const char *const ppsz_sout_options[] = {
71 "packet-size",
72 "caching",
73 "buffer-size",
74 "ssrc",
75 NULL
78 typedef struct
80 struct rist_flow *flow;
81 uint16_t rtp_counter;
82 char receiver_name[MAX_CNAME];
83 uint64_t last_rtcp_tx;
84 vlc_thread_t ristthread;
85 vlc_thread_t senderthread;
86 size_t i_packet_size;
87 bool b_mtu_warning;
88 vlc_mutex_t lock;
89 vlc_mutex_t fd_lock;
90 block_t *p_pktbuffer;
91 uint64_t i_ticks_caching;
92 uint32_t ssrc;
93 block_fifo_t *p_fifo;
94 /* stats variables */
95 uint64_t i_last_stat;
96 uint32_t i_retransmit_packets;
97 uint32_t i_total_packets;
98 } sout_access_out_sys_t;
100 static struct rist_flow *rist_init_tx()
102 struct rist_flow *flow = calloc(1, sizeof(struct rist_flow));
103 if (!flow)
104 return NULL;
106 flow->reset = 1;
107 flow->buffer = calloc(RIST_QUEUE_SIZE, sizeof(struct rtp_pkt));
108 if ( unlikely( flow->buffer == NULL ) )
110 free(flow);
111 return NULL;
113 flow->fd_out = -1;
114 flow->fd_rtcp = -1;
116 return flow;
119 static struct rist_flow *rist_udp_transmitter(sout_access_out_t *p_access, char *psz_dst_server,
120 int i_dst_port)
122 struct rist_flow *flow;
123 flow = rist_init_tx();
124 if (!flow)
125 return NULL;
127 flow->fd_out = net_ConnectDgram(p_access, psz_dst_server, i_dst_port, -1, IPPROTO_UDP );
128 if (flow->fd_out < 0)
130 msg_Err( p_access, "cannot open output socket" );
131 goto fail;
134 flow->fd_rtcp = net_ConnectDgram(p_access, psz_dst_server, i_dst_port + 1, -1, IPPROTO_UDP );
135 if (flow->fd_rtcp < 0)
137 msg_Err( p_access, "cannot open nack socket" );
138 goto fail;
141 populate_cname(flow->fd_rtcp, flow->cname);
142 msg_Info(p_access, "our cname is %s", flow->cname);
144 return flow;
146 fail:
147 if (flow->fd_out != -1)
148 vlc_close(flow->fd_out);
149 if (flow->fd_rtcp != -1)
150 vlc_close(flow->fd_rtcp);
151 free(flow->buffer);
152 free(flow);
153 return NULL;
156 static void rist_retransmit(sout_access_out_t *p_access, struct rist_flow *flow, uint16_t seq)
158 sout_access_out_sys_t *p_sys = p_access->p_sys;
159 struct rtp_pkt *pkt = &(flow->buffer[seq]);
160 if (pkt->buffer == NULL)
162 msg_Err(p_access, "RIST recovery: missing requested packet %d, buffer not yet full", seq);
163 return;
166 /* Mark SSID for retransmission (change the last bit of the ssrc to 1) */
167 pkt->buffer->p_buffer[11] |= (1 << 0);
168 #ifdef TEST_PACKET_LOSS
169 # warning COMPILED WITH SELF INFLICTED PACKET LOSS
170 if ((flow->packets_count % 14) == 0) {
171 return;
173 #endif
174 uint32_t rtp_age = flow->hi_timestamp - pkt->rtp_ts;
175 uint64_t age = ts_get_from_rtp(rtp_age)/1000;
176 if (flow->rtp_latency > 0 && rtp_age > flow->rtp_latency)
178 msg_Err(p_access, " Not Sending Nack #%d, too old (age %"PRId64" ms), current seq is:" \
179 " [%d]. Perhaps you should increase the buffer-size ...", seq, age, flow->wi);
181 else
183 msg_Dbg(p_access, " Sending Nack #%d (age %"PRId64" ms), current seq is: [%d]",
184 seq, age, flow->wi);
185 p_sys->i_retransmit_packets++;
186 vlc_mutex_lock( &p_sys->fd_lock );
187 if (rist_Write(flow->fd_out, pkt->buffer->p_buffer, pkt->buffer->i_buffer)
188 != (ssize_t)pkt->buffer->i_buffer) {
189 msg_Err(p_access, "Error sending retransmitted packet after 2 tries ...");
191 vlc_mutex_unlock( &p_sys->fd_lock );
195 static void process_nack(sout_access_out_t *p_access, uint8_t ptype, uint16_t nrecords,
196 struct rist_flow *flow, uint8_t *pkt)
198 sout_access_out_sys_t *p_sys = p_access->p_sys;
199 int i,j;
201 /*msg_Info(p_access, " Nack (BbRR), %d record(s), Window: [%d:%d-->%d]", nrecords,
202 flow->ri, flow->wi, flow->wi-flow->ri);*/
204 if (ptype == RTCP_PT_RTPFR)
206 uint8_t pi_ssrc[4];
207 rtcp_fb_get_ssrc_media_src(pkt, pi_ssrc);
208 if (memcmp(pi_ssrc, "RIST", 4) != 0)
210 msg_Info(p_access, " Ignoring Nack with name %s", pi_ssrc);
211 return; /* Ignore app-type not RIST */
214 for (i = 0; i < (nrecords-2); i++) {
215 uint16_t missing;
216 uint16_t additional;
217 uint8_t *rtp_nack_record = (pkt + 12 + i * 4);
218 missing = rtcp_fb_nack_get_range_start(rtp_nack_record);
219 additional = rtcp_fb_nack_get_range_extra(rtp_nack_record);
220 /*msg_Info(p_access, " Nack (Range), %d, current seq is: [%d]", missing, flow->wi);*/
221 vlc_mutex_lock( &p_sys->lock );
222 rist_retransmit(p_access, flow, missing);
223 for (j = 0; j < additional; j++) {
224 rist_retransmit(p_access, flow, missing + j + 1);
226 vlc_mutex_unlock( &p_sys->lock );
229 else if (ptype == RTCP_PT_RTPFB)
231 for (i = 0; i < (nrecords-2); i++) {
232 uint16_t missing;
233 uint16_t bitmask;
234 uint8_t *rtp_nack_record = (pkt + 12 + i * 4);
235 missing = rtcp_fb_nack_get_packet_id(rtp_nack_record);
236 bitmask = rtcp_fb_nack_get_bitmask_lost(rtp_nack_record);
237 /*msg_Info(p_access, " Nack (Bitmask), %d, current seq is: [%d]", missing, flow->wi);*/
238 vlc_mutex_lock( &p_sys->lock );
239 rist_retransmit(p_access, flow, missing);
240 for (j = 0; j < 16; j++) {
241 if ((bitmask & (1 << j)) == (1 << j)) {
242 rist_retransmit(p_access, flow, missing + j + 1);
245 vlc_mutex_unlock( &p_sys->lock );
248 else
250 msg_Err(p_access, " !!! Wrong feedback. Ptype is %02x!=%02x, FMT: %02x", ptype,
251 RTCP_PT_RTPFR, rtcp_fb_get_fmt(pkt));
255 static void rist_rtcp_recv(sout_access_out_t *p_access, struct rist_flow *flow, uint8_t *pkt_raw,
256 size_t len)
258 sout_access_out_sys_t *p_sys = p_access->p_sys;
259 uint8_t *pkt = pkt_raw;
260 uint8_t ptype;
261 uint16_t processed_bytes = 0;
262 uint16_t records;
264 while (processed_bytes < len) {
265 pkt = pkt_raw + processed_bytes;
266 /* safety checks */
267 uint16_t bytes_left = len - processed_bytes + 1;
268 if ( bytes_left < 4 )
270 /* we must have at least 4 bytes */
271 msg_Err(p_access, "Rist rtcp packet must have at least 4 bytes, we have %d",
272 bytes_left);
273 return;
275 else if (!rtp_check_hdr(pkt))
277 /* check for a valid rtp header */
278 msg_Err(p_access, "Malformed feedback packet starting with %02x, ignoring.", pkt[0]);
279 return;
282 ptype = rtcp_get_pt(pkt);
283 records = rtcp_get_length(pkt);
284 uint16_t bytes = (uint16_t)(4 * (1 + records));
285 if (bytes > bytes_left)
287 /* check for a sane number of bytes */
288 msg_Err(p_access, "Malformed feedback packet, wrong len %d, expecting %u bytes in the" \
289 " packet, got a buffer of %u bytes. ptype = %d", rtcp_get_length(pkt), bytes,
290 bytes_left, ptype);
291 return;
294 switch(ptype) {
295 case RTCP_PT_RTPFR:
296 case RTCP_PT_RTPFB:
297 process_nack(p_access, ptype, records, flow, pkt);
298 break;
300 case RTCP_PT_RR:
301 /*process_rr(f, pkt, len);*/
302 break;
304 case RTCP_PT_SDES:
306 int8_t name_length = rtcp_sdes_get_name_length(pkt);
307 if (name_length > bytes_left)
309 /* check for a sane number of bytes */
310 msg_Err(p_access, "Malformed SDES packet, wrong cname len %u, got a " \
311 "buffer of %u bytes.", name_length, bytes_left);
312 return;
314 if (memcmp(pkt + RTCP_SDES_SIZE, p_sys->receiver_name, name_length) != 0)
316 memcpy(p_sys->receiver_name, pkt + RTCP_SDES_SIZE, name_length);
317 msg_Info(p_access, "Receiver name: %s", p_sys->receiver_name);
320 break;
322 case RTCP_PT_SR:
323 break;
325 default:
326 msg_Err(p_access, " Unrecognized RTCP packet with PTYPE=%02x!!", ptype);
328 processed_bytes += bytes;
332 static void rist_rtcp_send(sout_access_out_t *p_access)
334 sout_access_out_sys_t *p_sys = p_access->p_sys;
335 struct rist_flow *flow = p_sys->flow;
336 uint8_t rtcp_buf[RTCP_SR_SIZE + RTCP_SDES_SIZE + MAX_CNAME] = { };
337 struct timeval tv;
338 int r;
339 uint64_t fractions;
340 uint16_t namelen = strlen(flow->cname) + 1;
341 gettimeofday(&tv, NULL);
343 /* Populate SR for sender report */
344 uint8_t *p_sr = rtcp_buf;
345 rtp_set_hdr(p_sr);
346 rtcp_sr_set_pt(p_sr);
347 rtcp_sr_set_length(p_sr, 6);
348 rtcp_fb_set_int_ssrc_pkt_sender(p_sr, p_sys->ssrc);
349 rtcp_sr_set_ntp_time_msw(p_sr, tv.tv_sec + SEVENTY_YEARS_OFFSET);
350 fractions = (uint64_t)tv.tv_usec;
351 fractions <<= 32ULL;
352 fractions /= 1000000ULL;
353 rtcp_sr_set_ntp_time_lsw(p_sr, (uint32_t)fractions);
354 rtcp_sr_set_rtp_time(p_sr, rtp_get_ts(vlc_tick_now()));
355 vlc_mutex_lock( &p_sys->lock );
356 rtcp_sr_set_packet_count(p_sr, flow->packets_count);
357 rtcp_sr_set_octet_count(p_sr, flow->bytes_count);
358 vlc_mutex_unlock( &p_sys->lock );
360 /* Populate SDES for sender description */
361 uint8_t *p_sdes = (rtcp_buf + RTCP_SR_SIZE);
362 /* we need to make sure it is a multiple of 4, pad if necessary */
363 if ((namelen - 2) & 0x3)
364 namelen = ((((namelen - 2) >> 2) + 1) << 2) + 2;
365 rtp_set_hdr(p_sdes);
366 rtp_set_cc(p_sdes, 1); /* Actually it is source count in this case */
367 rtcp_sdes_set_pt(p_sdes);
368 rtcp_set_length(p_sdes, (namelen >> 2) + 2);
369 rtcp_sdes_set_cname(p_sdes, 1);
370 rtcp_sdes_set_name_length(p_sdes, strlen(flow->cname));
371 p_sdes += RTCP_SDES_SIZE;
372 strlcpy((char *)p_sdes, flow->cname, namelen);
374 /* Send the rtcp message */
375 r = send(flow->fd_rtcp, rtcp_buf, RTCP_SR_SIZE + RTCP_SDES_SIZE + namelen, 0);
376 (void)r;
379 static void *rist_thread(void *data)
381 sout_access_out_t *p_access = data;
382 sout_access_out_sys_t *p_sys = p_access->p_sys;
383 uint64_t now;
384 uint8_t pkt[RTP_PKT_SIZE];
385 struct pollfd pfd[1];
386 int ret;
387 int r;
389 pfd[0].fd = p_sys->flow->fd_rtcp;
390 pfd[0].events = POLLIN;
392 for (;;) {
393 ret = poll(pfd, 1, RTCP_INTERVAL >> 1);
394 int canc = vlc_savecancel();
395 if (ret > 0)
397 if (pfd[0].revents & POLLIN)
399 r = rist_Read(p_sys->flow->fd_rtcp, pkt, RTP_PKT_SIZE);
400 if (r == RTP_PKT_SIZE) {
401 msg_Err(p_access, "Rist RTCP messsage is too big (%d bytes) and was probably " \
402 "cut, please keep it under %d bytes", r, RTP_PKT_SIZE);
404 if (unlikely(r == -1)) {
405 msg_Err(p_access, "socket %d error: %s\n", p_sys->flow->fd_rtcp,
406 gai_strerror(errno));
408 else {
409 rist_rtcp_recv(p_access, p_sys->flow, pkt, r);
414 /* And, in any case: */
415 now = vlc_tick_now();
416 if ((now - p_sys->last_rtcp_tx) > VLC_TICK_FROM_MS(RTCP_INTERVAL))
418 rist_rtcp_send(p_access);
419 p_sys->last_rtcp_tx = now;
421 vlc_restorecancel (canc);
424 return NULL;
427 /****************************************************************************
428 * RTP send
429 ****************************************************************************/
430 static void* ThreadSend( void *data )
432 sout_access_out_t *p_access = data;
433 sout_access_out_sys_t *p_sys = p_access->p_sys;
434 vlc_tick_t i_caching = p_sys->i_ticks_caching;
435 struct rist_flow *flow = p_sys->flow;
437 for (;;)
439 ssize_t len = 0;
440 uint16_t seq = 0;
441 uint32_t pkt_ts = 0;
442 block_t *out = block_FifoGet( p_sys->p_fifo );
444 block_cleanup_push( out );
445 vlc_tick_wait (out->i_dts + i_caching);
446 vlc_cleanup_pop();
448 len = out->i_buffer;
449 int canc = vlc_savecancel();
451 seq = rtp_get_seqnum(out->p_buffer);
452 pkt_ts = rtp_get_timestamp(out->p_buffer);
454 vlc_mutex_lock( &p_sys->fd_lock );
455 #ifdef TEST_PACKET_LOSS
456 # warning COMPILED WITH SELF INFLICTED PACKET LOSS
457 if ((seq % 14) == 0) {
458 /*msg_Err(p_access, "Dropped packet with seq number %d ...", seq);*/
460 else if (rist_Write(flow->fd_out, out->p_buffer, len) != len) {
461 msg_Err(p_access, "Error sending data packet after 2 tries ...");
463 #else
464 if (rist_Write(flow->fd_out, out->p_buffer, len) != len) {
465 msg_Err(p_access, "Error sending data packet after 2 tries ...");
467 #endif
468 vlc_mutex_unlock( &p_sys->fd_lock );
470 /* Insert Into Queue */
471 vlc_mutex_lock( &p_sys->lock );
472 /* Always replace the existing one with the new one */
473 struct rtp_pkt *pkt;
474 pkt = &(flow->buffer[seq]);
475 if (pkt->buffer)
477 block_Release(pkt->buffer);
478 pkt->buffer = NULL;
480 pkt->rtp_ts = pkt_ts;
481 pkt->buffer = out;
483 if (flow->reset == 1)
485 msg_Info(p_access, "Traffic detected");
486 /* First packet in the queue */
487 flow->reset = 0;
489 flow->wi = seq;
490 flow->hi_timestamp = pkt_ts;
491 /* Stats for RTCP feedback packets */
492 flow->packets_count++;
493 flow->bytes_count += len;
494 flow->last_out = seq;
495 vlc_mutex_unlock( &p_sys->lock );
497 /* We print out the stats once per second */
498 uint64_t now = vlc_tick_now();
499 uint64_t interval = (now - p_sys->i_last_stat);
500 if ( interval > VLC_TICK_FROM_MS(STATS_INTERVAL) )
502 if (p_sys->i_retransmit_packets > 0)
504 float quality = 100;
505 if (p_sys->i_total_packets > 0)
506 quality = (float)100 - (float)100*(float)(p_sys->i_retransmit_packets)
507 /(float)p_sys->i_total_packets;
508 msg_Info(p_access, "STATS: Total %u, Retransmitted %u, Link Quality %.2f%%",
509 p_sys->i_total_packets, p_sys->i_retransmit_packets, quality);
511 p_sys->i_last_stat = now;
512 p_sys->i_retransmit_packets = 0;
513 p_sys->i_total_packets = 0;
515 p_sys->i_total_packets++;
517 vlc_restorecancel (canc);
519 return NULL;
522 static void SendtoFIFO( sout_access_out_t *p_access, block_t *buffer )
524 sout_access_out_sys_t *p_sys = p_access->p_sys;
525 uint16_t seq = p_sys->rtp_counter++;
527 /* Set fresh rtp header data */
528 uint8_t *bufhdr = buffer->p_buffer;
529 rtp_set_hdr(bufhdr);
530 rtp_set_type(bufhdr, MPEG_II_TRANSPORT_STREAM);
531 rtp_set_seqnum(bufhdr, seq);
532 rtp_set_int_ssrc(bufhdr, p_sys->ssrc);
533 uint32_t pkt_ts = rtp_get_ts(buffer->i_dts);
534 rtp_set_timestamp(bufhdr, pkt_ts);
536 block_t *pkt = block_Duplicate(buffer);
537 block_FifoPut( p_sys->p_fifo, pkt );
540 static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
542 sout_access_out_sys_t *p_sys = p_access->p_sys;
543 int i_len = 0;
545 while( p_buffer )
547 block_t *p_next;
548 int i_block_split = 0;
550 if( !p_sys->b_mtu_warning && p_buffer->i_buffer > p_sys->i_packet_size )
552 msg_Warn( p_access, "Buffer data size (%zu) > configured packet size (%zu), you " \
553 "should probably increase the configured packet size", p_buffer->i_buffer,
554 p_sys->i_packet_size );
555 p_sys->b_mtu_warning = true;
558 /* Temp buffer is already too large, flush */
559 if( p_sys->p_pktbuffer->i_buffer + p_buffer->i_buffer > p_sys->i_packet_size )
561 SendtoFIFO(p_access, p_sys->p_pktbuffer);
562 p_sys->p_pktbuffer->i_buffer = RTP_HEADER_SIZE;
565 i_len += p_buffer->i_buffer;
567 while( p_buffer->i_buffer )
570 size_t i_write = __MIN( p_buffer->i_buffer, p_sys->i_packet_size );
572 i_block_split++;
574 if( p_sys->p_pktbuffer->i_buffer == RTP_HEADER_SIZE )
576 p_sys->p_pktbuffer->i_dts = p_buffer->i_dts;
579 memcpy( p_sys->p_pktbuffer->p_buffer + p_sys->p_pktbuffer->i_buffer,
580 p_buffer->p_buffer, i_write );
582 p_sys->p_pktbuffer->i_buffer += i_write;
583 p_buffer->p_buffer += i_write;
584 p_buffer->i_buffer -= i_write;
586 /* Flush if we reached the target size for the case of block size < target packet size.
587 * Also flush when we are in block_split > 1 for the case when the block_size is
588 * larger than the packet-size because we need to continue the inner loop */
589 if( p_sys->p_pktbuffer->i_buffer == p_sys->i_packet_size || i_block_split > 1 )
591 SendtoFIFO(p_access, p_sys->p_pktbuffer);
592 p_sys->p_pktbuffer->i_buffer = RTP_HEADER_SIZE;
597 p_next = p_buffer->p_next;
598 block_Release( p_buffer );
599 p_buffer = p_next;
603 if ( i_len <= 0 ) {
604 block_ChainRelease( p_buffer );
606 return i_len;
609 static int Control( sout_access_out_t *p_access, int i_query, va_list args )
611 VLC_UNUSED( p_access );
613 int i_ret = VLC_SUCCESS;
615 switch( i_query )
617 case ACCESS_OUT_CONTROLS_PACE:
618 *va_arg( args, bool * ) = false;
619 break;
621 default:
622 i_ret = VLC_EGENERIC;
623 break;
626 return i_ret;
629 static void Clean( sout_access_out_t *p_access )
631 sout_access_out_sys_t *p_sys = p_access->p_sys;
633 if( likely(p_sys->p_fifo != NULL) )
634 block_FifoRelease( p_sys->p_fifo );
636 if ( p_sys->flow )
638 if (p_sys->flow->fd_out >= 0) {
639 net_Close (p_sys->flow->fd_out);
641 if (p_sys->flow->fd_nack >= 0) {
642 net_Close (p_sys->flow->fd_rtcp);
644 for (int i=0; i<RIST_QUEUE_SIZE; i++) {
645 struct rtp_pkt *pkt = &(p_sys->flow->buffer[i]);
646 if (pkt->buffer)
648 block_Release(pkt->buffer);
649 pkt->buffer = NULL;
652 free(p_sys->flow->buffer);
653 free(p_sys->flow);
656 vlc_mutex_destroy( &p_sys->lock );
657 vlc_mutex_destroy( &p_sys->fd_lock );
658 if (p_sys->p_pktbuffer)
659 block_Release(p_sys->p_pktbuffer);
662 static void Close( vlc_object_t * p_this )
664 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
665 sout_access_out_sys_t *p_sys = p_access->p_sys;
667 vlc_cancel(p_sys->ristthread);
668 vlc_cancel(p_sys->senderthread);
670 vlc_join(p_sys->ristthread, NULL);
671 vlc_join(p_sys->senderthread, NULL);
673 Clean( p_access );
676 static int Open( vlc_object_t *p_this )
678 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
679 sout_access_out_sys_t *p_sys = NULL;
681 if (var_Create ( p_access, "dst-port", VLC_VAR_INTEGER )
682 || var_Create ( p_access, "src-port", VLC_VAR_INTEGER )
683 || var_Create ( p_access, "dst-addr", VLC_VAR_STRING )
684 || var_Create ( p_access, "src-addr", VLC_VAR_STRING ) )
686 msg_Err( p_access, "Valid network information is required." );
687 return VLC_ENOMEM;
690 config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
692 p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) );
693 if( unlikely( p_sys == NULL ) )
694 return VLC_ENOMEM;
696 int i_dst_port = RIST_DEFAULT_PORT;
697 char *psz_dst_addr;
698 char *psz_parser = psz_dst_addr = strdup( p_access->psz_path );
699 if( !psz_dst_addr )
700 return VLC_ENOMEM;
702 if ( psz_parser[0] == '[' )
703 psz_parser = strchr( psz_parser, ']' );
705 psz_parser = strchr( psz_parser ? psz_parser : psz_dst_addr, ':' );
706 if ( psz_parser != NULL )
708 *psz_parser++ = '\0';
709 i_dst_port = atoi( psz_parser );
712 vlc_mutex_init( &p_sys->lock );
713 vlc_mutex_init( &p_sys->fd_lock );
715 msg_Info(p_access, "Connecting RIST output to %s:%d and %s:%d", psz_dst_addr, i_dst_port,
716 psz_dst_addr, i_dst_port+1);
717 struct rist_flow *flow = rist_udp_transmitter(p_access, psz_dst_addr, i_dst_port);
718 free (psz_dst_addr);
719 if (!flow)
720 goto failed;
722 p_sys->flow = flow;
723 flow->latency = var_InheritInteger(p_access, SOUT_CFG_PREFIX "buffer-size");
724 flow->rtp_latency = rtp_get_ts(VLC_TICK_FROM_MS(flow->latency));
725 p_sys->ssrc = var_InheritInteger(p_access, SOUT_CFG_PREFIX "ssrc");
726 if (p_sys->ssrc == 0) {
727 vlc_rand_bytes(&p_sys->ssrc, 4);
729 /* Last bit of ssrc must be 0 for normal data and 1 for retries */
730 p_sys->ssrc &= ~(1 << 0);
732 msg_Info(p_access, "SSRC: 0x%08X", p_sys->ssrc);
733 p_sys->i_ticks_caching = VLC_TICK_FROM_MS(var_InheritInteger( p_access,
734 SOUT_CFG_PREFIX "caching"));
735 p_sys->i_packet_size = var_InheritInteger(p_access, SOUT_CFG_PREFIX "packet-size" );
736 p_sys->p_fifo = block_FifoNew();
737 if( unlikely(p_sys->p_fifo == NULL) )
738 goto failed;
739 p_sys->p_pktbuffer = block_Alloc( p_sys->i_packet_size );
740 if( unlikely(p_sys->p_pktbuffer == NULL) )
741 goto failed;
743 p_sys->p_pktbuffer->i_buffer = RTP_HEADER_SIZE;
745 p_access->p_sys = p_sys;
747 if( vlc_clone(&p_sys->senderthread, ThreadSend, p_access, VLC_THREAD_PRIORITY_HIGHEST ) )
749 msg_Err(p_access, "Failed to create sender thread.");
750 goto failed;
753 if (vlc_clone(&p_sys->ristthread, rist_thread, p_access, VLC_THREAD_PRIORITY_INPUT))
755 msg_Err(p_access, "Failed to create worker thread.");
756 vlc_cancel(p_sys->senderthread);
757 vlc_join(p_sys->senderthread, NULL);
758 goto failed;
761 p_access->pf_write = Write;
762 p_access->pf_control = Control;
764 return VLC_SUCCESS;
766 failed:
767 Clean( p_access );
768 return VLC_EGENERIC;
771 #define CACHING_TEXT N_("RIST data output caching size (ms)")
772 #define CACHING_LONGTEXT N_( \
773 "Having this cache will guarantee that the packets going out are " \
774 "delivered at a spacing determined by the chain timestamps thus ensuring " \
775 "a near jitter free output. Be aware that this setting will also add to " \
776 "the overall latency of the stream." )
778 #define BUFFER_TEXT N_("RIST retry-buffer queue size (ms)")
779 #define BUFFER_LONGTEXT N_( \
780 "This must match the buffer size (latency) configured on the server side. If you " \
781 "are not sure, leave the default of 0 which will set it the maximum " \
782 "value and will use about 100MB of RAM" )
784 #define SSRC_TEXT N_("SSRC used in RTP output (default is random, i.e. 0)")
785 #define SSRC_LONGTEXT N_( \
786 "Use this setting to specify a known SSRC for the RTP header. This is only useful " \
787 "if your receiver acts on it. When using VLC as receiver, it is not." )
789 /* Module descriptor */
790 vlc_module_begin()
792 set_shortname( N_("RIST") )
793 set_description( N_("RIST stream output") )
794 set_category( CAT_SOUT )
795 set_subcategory( SUBCAT_SOUT_ACO )
797 add_integer( SOUT_CFG_PREFIX "packet-size", RIST_TARGET_PACKET_SIZE,
798 N_("RIST target packet size (bytes)"), NULL, true )
799 add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_CACHING_DELAY,
800 CACHING_TEXT, CACHING_LONGTEXT, true )
801 add_integer( SOUT_CFG_PREFIX "buffer-size", DEFAULT_BUFFER_SIZE,
802 BUFFER_TEXT, BUFFER_LONGTEXT, true )
803 add_integer( SOUT_CFG_PREFIX "ssrc", 0,
804 SSRC_TEXT, SSRC_LONGTEXT, true )
806 set_capability( "sout access", 0 )
807 add_shortcut( "rist", "tr06" )
809 set_callbacks( Open, Close )
811 vlc_module_end ()