access: add RIST module
[vlc.git] / modules / access / rist.h
blob256c1fcf638deab33afa080613d2f3b4bcc1ffea
1 /*****************************************************************************
2 * rist.h: RIST (Reliable Internet Stream Transport) helper
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 #include <stdint.h>
26 #ifdef HAVE_ARPA_INET_H
27 #include <arpa/inet.h>
28 #endif
29 #include <errno.h>
30 #include <assert.h>
32 /*****************************************************************************
33 * Public API
34 *****************************************************************************/
36 /* RIST */
38 /* RTP header format (RFC 3550) */
40 0 1 2 3
41 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
42 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 |V=2|P|X| CC |M| PT | sequence number |
44 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 | timestamp |
46 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 | synchronization source (SSRC) identifier |
48 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
49 | contributing source (CSRC) identifiers |
50 | .... |
51 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 #define RIST_QUEUE_SIZE 65536
55 #define RTP_PKT_SIZE (1472)
57 #define RTCP_INTERVAL 75 /*ms*/
59 #define SEVENTY_YEARS_OFFSET (2208988800ULL)
60 #define MAX_NACKS 32
61 #define MAX_CNAME 128
62 #define RTCP_EMPTY_RR_SIZE 8
64 #define RTCP_PT_RTPFR 204
66 struct rtp_pkt {
67 uint32_t rtp_ts;
68 struct block_t *buffer;
71 /* RIST NACK header format */
73 0 1 2 3
74 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
75 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 | SNBase low bits | Length recovery |
77 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78 |E| PT recovery | Mask |
79 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 | TS recovery |
81 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82 |N|D|type |index| Offset | NA |SNBase ext bits|
83 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 struct rist_flow {
87 uint8_t reset;
88 struct rtp_pkt *buffer;
89 uint32_t qsize;
90 uint32_t last_out;
91 uint32_t ssrc;
92 char cname[MAX_CNAME];
93 struct sockaddr_storage peer_sockaddr;
94 socklen_t peer_socklen;
95 uint16_t ri,
96 wi;
97 int fd_in;
98 int fd_out;
99 int fd_rtcp;
100 int fd_nack;
101 uint8_t nacks_retries[RIST_QUEUE_SIZE];
102 uint32_t hi_timestamp;
103 uint64_t feedback_time;
104 uint32_t latency;
105 uint32_t rtp_latency;
106 uint32_t retry_interval;
107 uint32_t reorder_buffer;
108 uint8_t max_retries;
109 uint32_t packets_count;
110 uint32_t bytes_count;
113 static inline uint16_t rtcp_fb_nack_get_range_start(const uint8_t *p_rtcp_fb_nack)
115 return (p_rtcp_fb_nack[0] << 8) | p_rtcp_fb_nack[1];
118 static inline uint16_t rtcp_fb_nack_get_range_extra(const uint8_t *p_rtcp_fb_nack)
120 return (p_rtcp_fb_nack[2] << 8) | p_rtcp_fb_nack[3];
123 static inline void rtcp_fb_nack_set_range_start(uint8_t *p_rtcp_fb_nack,
124 uint16_t start)
126 p_rtcp_fb_nack[0] = (start >> 8) & 0xff;
127 p_rtcp_fb_nack[1] = start & 0xff;
130 static inline void rtcp_fb_nack_set_range_extra(uint8_t *p_rtcp_fb_nack,
131 uint16_t extra)
133 p_rtcp_fb_nack[2] = (extra >> 8) & 0xff;
134 p_rtcp_fb_nack[3] = extra & 0xff;
137 static inline void populate_cname(int fd, char *identifier)
139 /* Set the CNAME Identifier as host@ip:port and fallback to hostname if needed */
140 char hostname[MAX_CNAME];
141 struct sockaddr_storage peer_sockaddr;
142 int name_length = 0;
143 socklen_t peer_socklen;
144 int ret_hostname = gethostname(hostname, MAX_CNAME);
145 if (ret_hostname == -1)
146 snprintf(hostname, MAX_CNAME, "UnknownHost");
147 int ret_sockname = getsockname(fd, (struct sockaddr *)&peer_sockaddr, &peer_socklen);
148 if (ret_sockname == 0)
150 struct sockaddr *peer = (struct sockaddr *)&peer_sockaddr;
151 if (peer->sa_family == AF_INET) {
152 struct sockaddr_in *xin = (void*)peer;
153 name_length = snprintf(identifier, MAX_CNAME, "%s@%s:%u", hostname,
154 inet_ntoa(xin->sin_addr), ntohs(xin->sin_port));
155 if (name_length >= MAX_CNAME)
156 identifier[MAX_CNAME-1] = 0;
157 } else if (peer->sa_family == AF_INET6) {
158 struct sockaddr_in6 *xin6 = (void*)peer;
159 char str[INET6_ADDRSTRLEN];
160 inet_ntop(xin6->sin6_family, &xin6->sin6_addr, str, sizeof(struct in6_addr));
161 name_length = snprintf(identifier, MAX_CNAME, "%s@%s:%u", hostname,
162 str, ntohs(xin6->sin6_port));
163 if (name_length >= MAX_CNAME)
164 identifier[MAX_CNAME-1] = 0;
167 if (name_length == 0)
169 name_length = snprintf(identifier, MAX_CNAME, "%s", hostname);
170 if (name_length >= MAX_CNAME)
171 identifier[MAX_CNAME-1] = 0;
175 static inline uint32_t rtp_get_ts( vlc_tick_t i_pts )
177 unsigned i_clock_rate = 90000;
178 /* This is an overflow-proof way of doing:
179 * return i_pts * (int64_t)i_clock_rate / CLOCK_FREQ;
181 * NOTE: this plays nice with offsets because the (equivalent)
182 * calculations are linear. */
183 lldiv_t q = lldiv(i_pts, CLOCK_FREQ);
184 return q.quot * (int64_t)i_clock_rate
185 + q.rem * (int64_t)i_clock_rate / CLOCK_FREQ;
188 static inline vlc_tick_t ts_get_from_rtp( uint32_t i_rtp_ts )
190 unsigned i_clock_rate = 90000;
191 return (vlc_tick_t)i_rtp_ts * (vlc_tick_t)(CLOCK_FREQ/i_clock_rate);
194 static inline ssize_t rist_ReadFrom_i11e(int fd, void *buf, size_t len, struct sockaddr *peer,
195 socklen_t *slen)
197 ssize_t ret = -1;
199 if (peer == NULL)
200 ret = vlc_recv_i11e(fd, buf, len, 0);
201 else
202 ret = vlc_recvfrom_i11e(fd, buf, len, 0, peer, slen);
204 if (ret == -1)
206 switch (errno)
208 case EAGAIN:
209 case EINTR:
210 if (vlc_killed())
211 return ret;
213 /* retry one time */
214 if (peer == NULL)
215 ret = vlc_recv_i11e(fd, buf, len, 0);
216 else
217 ret = vlc_recvfrom_i11e(fd, buf, len, 0, peer, slen);
218 default:
219 break;
222 return ret;
225 static inline ssize_t rist_Read_i11e(int fd, void *buf, size_t len)
227 return rist_ReadFrom_i11e(fd, buf, len, NULL, NULL);
230 static inline ssize_t rist_ReadFrom(int fd, void *buf, size_t len, struct sockaddr *peer,
231 socklen_t *slen)
233 ssize_t ret = -1;
235 if (peer == NULL)
236 ret = recv(fd, buf, len, 0);
237 else
238 ret = recvfrom(fd, buf, len, 0, peer, slen);
240 if (ret == -1)
242 switch (errno)
244 case EAGAIN:
245 case EINTR:
246 /* retry one time */
247 if (peer == NULL)
248 ret = recv(fd, buf, len, 0);
249 else
250 ret = recvfrom(fd, buf, len, 0, peer, slen);
251 default:
252 break;
255 return ret;
258 static inline ssize_t rist_Read(int fd, void *buf, size_t len)
260 return rist_ReadFrom(fd, buf, len, NULL, NULL);
263 static inline ssize_t rist_WriteTo_i11e(int fd, const void *buf, size_t len,
264 const struct sockaddr *peer, socklen_t slen)
266 #ifdef _WIN32
267 # define ENOBUFS WSAENOBUFS
268 # define EAGAIN WSAEWOULDBLOCK
269 # define EWOULDBLOCK WSAEWOULDBLOCK
270 #endif
271 ssize_t r = -1;
272 if (slen == 0)
273 r = vlc_send_i11e( fd, buf, len, 0 );
274 else
275 r = vlc_sendto_i11e( fd, buf, len, 0, peer, slen );
276 if( r == -1
277 && net_errno != EAGAIN && net_errno != EWOULDBLOCK
278 && net_errno != ENOBUFS && net_errno != ENOMEM )
280 int type;
281 getsockopt( fd, SOL_SOCKET, SO_TYPE,
282 &type, &(socklen_t){ sizeof(type) });
283 if( type == SOCK_DGRAM )
285 /* ICMP soft error: ignore and retry */
286 if (slen == 0)
287 r = vlc_send_i11e( fd, buf, len, 0 );
288 else
289 r = vlc_sendto_i11e( fd, buf, len, 0, peer, slen );
292 return r;
295 static inline ssize_t rist_Write_i11e(int fd, const void *buf, size_t len)
297 return rist_WriteTo_i11e(fd, buf, len, NULL, 0);
300 static inline ssize_t rist_WriteTo(int fd, const void *buf, size_t len, const struct sockaddr *peer,
301 socklen_t slen)
303 #ifdef _WIN32
304 # define ENOBUFS WSAENOBUFS
305 # define EAGAIN WSAEWOULDBLOCK
306 # define EWOULDBLOCK WSAEWOULDBLOCK
307 #endif
308 ssize_t r = -1;
309 if (slen == 0)
310 r = send( fd, buf, len, 0 );
311 else
312 r = sendto( fd, buf, len, 0, peer, slen );
313 if( r == -1
314 && net_errno != EAGAIN && net_errno != EWOULDBLOCK
315 && net_errno != ENOBUFS && net_errno != ENOMEM )
317 int type;
318 getsockopt( fd, SOL_SOCKET, SO_TYPE,
319 &type, &(socklen_t){ sizeof(type) });
320 if( type == SOCK_DGRAM )
322 /* ICMP soft error: ignore and retry */
323 if (slen == 0)
324 r = send( fd, buf, len, 0 );
325 else
326 r = sendto( fd, buf, len, 0, peer, slen );
329 return r;
332 static inline ssize_t rist_Write(int fd, const void *buf, size_t len)
334 return rist_WriteTo(fd, buf, len, NULL, 0);