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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_interrupt.h>
32 #include <vlc_plugin.h>
34 #include <vlc_block.h>
35 #include <vlc_network.h>
36 #include <vlc_threads.h>
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
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
[] = {
80 struct rist_flow
*flow
;
82 char receiver_name
[MAX_CNAME
];
83 uint64_t last_rtcp_tx
;
84 vlc_thread_t ristthread
;
85 vlc_thread_t senderthread
;
91 uint64_t i_ticks_caching
;
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
));
107 flow
->buffer
= calloc(RIST_QUEUE_SIZE
, sizeof(struct rtp_pkt
));
108 if ( unlikely( flow
->buffer
== NULL
) )
119 static struct rist_flow
*rist_udp_transmitter(sout_access_out_t
*p_access
, char *psz_dst_server
,
122 struct rist_flow
*flow
;
123 flow
= rist_init_tx();
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" );
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" );
141 populate_cname(flow
->fd_rtcp
, flow
->cname
);
142 msg_Info(p_access
, "our cname is %s", flow
->cname
);
147 if (flow
->fd_out
!= -1)
148 vlc_close(flow
->fd_out
);
149 if (flow
->fd_rtcp
!= -1)
150 vlc_close(flow
->fd_rtcp
);
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
);
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) {
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
);
183 msg_Dbg(p_access
, " Sending Nack #%d (age %"PRId64
" ms), current seq is: [%d]",
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
;
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
)
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
++) {
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
++) {
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
);
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
,
258 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
259 uint8_t *pkt
= pkt_raw
;
261 uint16_t processed_bytes
= 0;
264 while (processed_bytes
< len
) {
265 pkt
= pkt_raw
+ processed_bytes
;
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",
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]);
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
,
297 process_nack(p_access
, ptype
, records
, flow
, pkt
);
301 /*process_rr(f, pkt, len);*/
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
);
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
);
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
] = { };
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
;
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
;
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;
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);
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
;
384 uint8_t pkt
[RTP_PKT_SIZE
];
385 struct pollfd pfd
[1];
389 pfd
[0].fd
= p_sys
->flow
->fd_rtcp
;
390 pfd
[0].events
= POLLIN
;
393 ret
= poll(pfd
, 1, RTCP_INTERVAL
>> 1);
394 int canc
= vlc_savecancel();
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
));
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
);
427 /****************************************************************************
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
;
442 block_t
*out
= block_FifoGet( p_sys
->p_fifo
);
444 block_cleanup_push( out
);
445 vlc_tick_wait (out
->i_dts
+ i_caching
);
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 ...");
464 if (rist_Write(flow
->fd_out
, out
->p_buffer
, len
) != len
) {
465 msg_Err(p_access
, "Error sending data packet after 2 tries ...");
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 */
474 pkt
= &(flow
->buffer
[seq
]);
477 block_Release(pkt
->buffer
);
480 pkt
->rtp_ts
= pkt_ts
;
483 if (flow
->reset
== 1)
485 msg_Info(p_access
, "Traffic detected");
486 /* First packet in the queue */
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)
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
);
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
;
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
;
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
);
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
);
604 block_ChainRelease( p_buffer
);
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
;
617 case ACCESS_OUT_CONTROLS_PACE
:
618 *va_arg( args
, bool * ) = false;
622 i_ret
= VLC_EGENERIC
;
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
);
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
]);
648 block_Release(pkt
->buffer
);
652 free(p_sys
->flow
->buffer
);
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
);
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." );
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
) )
696 int i_dst_port
= RIST_DEFAULT_PORT
;
698 char *psz_parser
= psz_dst_addr
= strdup( p_access
->psz_path
);
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
);
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
) )
739 p_sys
->p_pktbuffer
= block_Alloc( p_sys
->i_packet_size
);
740 if( unlikely(p_sys
->p_pktbuffer
== NULL
) )
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.");
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
);
761 p_access
->pf_write
= Write
;
762 p_access
->pf_control
= Control
;
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 */
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
)