2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * Haivision Open SRT (Secure Reliable Transport) protocol
26 #include "libavutil/mem.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/parseutils.h"
29 #include "libavutil/time.h"
34 #include "os_support.h"
36 #include "urldecode.h"
38 /* This is for MPEG-TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE (8 TS packets) */
39 #ifndef SRT_LIVE_DEFAULT_PAYLOAD_SIZE
40 #define SRT_LIVE_DEFAULT_PAYLOAD_SIZE 1316
43 /* This is the maximum payload size for Live mode, should you have a different payload type than MPEG-TS */
44 #ifndef SRT_LIVE_MAX_PAYLOAD_SIZE
45 #define SRT_LIVE_MAX_PAYLOAD_SIZE 1456
50 SRT_MODE_LISTENER
= 1,
51 SRT_MODE_RENDEZVOUS
= 2
54 typedef struct SRTContext
{
59 int64_t listen_timeout
;
66 #if SRT_VERSION_VALUE >= 0x010302
67 int enforced_encryption
;
81 int64_t connect_timeout
;
93 SRT_TRANSTYPE transtype
;
98 #define D AV_OPT_FLAG_DECODING_PARAM
99 #define E AV_OPT_FLAG_ENCODING_PARAM
100 #define OFFSET(x) offsetof(SRTContext, x)
101 static const AVOption libsrt_options
[] = {
102 { "timeout", "Timeout of socket I/O operations (in microseconds)", OFFSET(rw_timeout
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
103 { "listen_timeout", "Connection awaiting timeout (in microseconds)" , OFFSET(listen_timeout
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
104 { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
105 { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
106 { "pkt_size", "Maximum SRT packet size", OFFSET(payload_size
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE
, .flags
= D
|E
, .unit
= "payload_size" },
107 { "payload_size", "Maximum SRT packet size", OFFSET(payload_size
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE
, .flags
= D
|E
, .unit
= "payload_size" },
108 { "ts_size", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_LIVE_DEFAULT_PAYLOAD_SIZE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "payload_size" },
109 { "max_size", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_LIVE_MAX_PAYLOAD_SIZE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "payload_size" },
110 { "maxbw", "Maximum bandwidth (bytes per second) that the connection can use", OFFSET(maxbw
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
111 { "pbkeylen", "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)", OFFSET(pbkeylen
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 32, .flags
= D
|E
},
112 { "passphrase", "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto", OFFSET(passphrase
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
113 #if SRT_VERSION_VALUE >= 0x010302
114 { "enforced_encryption", "Enforces that both connection parties have the same passphrase set", OFFSET(enforced_encryption
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
115 { "kmrefreshrate", "The number of packets to be transmitted after which the encryption key is switched to a new key", OFFSET(kmrefreshrate
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
116 { "kmpreannounce", "The interval between when a new encryption key is sent and when switchover occurs", OFFSET(kmpreannounce
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
117 { "snddropdelay", "The sender's extra delay(in microseconds) before dropping packets", OFFSET(snddropdelay
), AV_OPT_TYPE_INT64
, { .i64
= -2 }, -2, INT64_MAX
, .flags
= D
|E
},
119 { "mss", "The Maximum Segment Size", OFFSET(mss
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 1500, .flags
= D
|E
},
120 { "ffs", "Flight flag size (window size) (in bytes)", OFFSET(ffs
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
121 { "ipttl", "IP Time To Live", OFFSET(ipttl
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 255, .flags
= D
|E
},
122 { "iptos", "IP Type of Service", OFFSET(iptos
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 255, .flags
= D
|E
},
123 { "inputbw", "Estimated input stream rate", OFFSET(inputbw
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
124 { "oheadbw", "MaxBW ceiling based on % over input stream rate", OFFSET(oheadbw
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 100, .flags
= D
|E
},
125 { "latency", "receiver delay (in microseconds) to absorb bursts of missed packet retransmissions", OFFSET(latency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
126 { "tsbpddelay", "deprecated, same effect as latency option", OFFSET(latency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
127 { "rcvlatency", "receive latency (in microseconds)", OFFSET(rcvlatency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
128 { "peerlatency", "peer latency (in microseconds)", OFFSET(peerlatency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
129 { "tlpktdrop", "Enable too-late pkt drop", OFFSET(tlpktdrop
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
130 { "nakreport", "Enable receiver to send periodic NAK reports", OFFSET(nakreport
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
131 { "connect_timeout", "Connect timeout(in milliseconds). Caller default: 3000, rendezvous (x 10)", OFFSET(connect_timeout
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
132 { "mode", "Connection mode (caller, listener, rendezvous)", OFFSET(mode
), AV_OPT_TYPE_INT
, { .i64
= SRT_MODE_CALLER
}, SRT_MODE_CALLER
, SRT_MODE_RENDEZVOUS
, .flags
= D
|E
, .unit
= "mode" },
133 { "caller", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_CALLER
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "mode" },
134 { "listener", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_LISTENER
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "mode" },
135 { "rendezvous", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_RENDEZVOUS
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "mode" },
136 { "sndbuf", "Send buffer size (in bytes)", OFFSET(sndbuf
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
137 { "rcvbuf", "Receive buffer size (in bytes)", OFFSET(rcvbuf
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
138 { "lossmaxttl", "Maximum possible packet reorder tolerance", OFFSET(lossmaxttl
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
139 { "minversion", "The minimum SRT version that is required from the peer", OFFSET(minversion
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
140 { "streamid", "A string of up to 512 characters that an Initiator can pass to a Responder", OFFSET(streamid
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
141 { "srt_streamid", "A string of up to 512 characters that an Initiator can pass to a Responder", OFFSET(streamid
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
142 { "smoother", "The type of Smoother used for the transmission for that socket", OFFSET(smoother
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
143 { "messageapi", "Enable message API", OFFSET(messageapi
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
144 { "transtype", "The transmission type for the socket", OFFSET(transtype
), AV_OPT_TYPE_INT
, { .i64
= SRTT_INVALID
}, SRTT_LIVE
, SRTT_INVALID
, .flags
= D
|E
, .unit
= "transtype" },
145 { "live", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRTT_LIVE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "transtype" },
146 { "file", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRTT_FILE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, .unit
= "transtype" },
147 { "linger", "Number of seconds that the socket waits for unsent data when closing", OFFSET(linger
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
148 { "tsbpd", "Timestamp-based packet delivery", OFFSET(tsbpd
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
152 static int libsrt_neterrno(URLContext
*h
)
155 int err
= srt_getlasterror(&os_errno
);
156 if (err
== SRT_EASYNCRCV
|| err
== SRT_EASYNCSND
)
157 return AVERROR(EAGAIN
);
158 av_log(h
, AV_LOG_ERROR
, "%s\n", srt_getlasterror_str());
159 return os_errno
? AVERROR(os_errno
) : AVERROR_UNKNOWN
;
162 static int libsrt_getsockopt(URLContext
*h
, int fd
, SRT_SOCKOPT optname
, const char * optnamestr
, void * optval
, int * optlen
)
164 if (srt_getsockopt(fd
, 0, optname
, optval
, optlen
) < 0) {
165 av_log(h
, AV_LOG_ERROR
, "failed to get option %s on socket: %s\n", optnamestr
, srt_getlasterror_str());
171 static int libsrt_socket_nonblock(int socket
, int enable
)
173 int ret
, blocking
= enable
? 0 : 1;
174 /* Setting SRTO_{SND,RCV}SYN options to 1 enable blocking mode, setting them to 0 enable non-blocking mode. */
175 ret
= srt_setsockopt(socket
, 0, SRTO_SNDSYN
, &blocking
, sizeof(blocking
));
178 return srt_setsockopt(socket
, 0, SRTO_RCVSYN
, &blocking
, sizeof(blocking
));
181 static int libsrt_epoll_create(URLContext
*h
, int fd
, int write
)
183 int modes
= SRT_EPOLL_ERR
| (write
? SRT_EPOLL_OUT
: SRT_EPOLL_IN
);
184 int eid
= srt_epoll_create();
186 return libsrt_neterrno(h
);
187 if (srt_epoll_add_usock(eid
, fd
, &modes
) < 0) {
188 srt_epoll_release(eid
);
189 return libsrt_neterrno(h
);
194 static int libsrt_network_wait_fd(URLContext
*h
, int eid
, int write
)
196 int ret
, len
= 1, errlen
= 1;
201 ret
= srt_epoll_wait(eid
, error
, &errlen
, ready
, &len
, POLLING_TIME
, 0, 0, 0, 0);
203 ret
= srt_epoll_wait(eid
, ready
, &len
, error
, &errlen
, POLLING_TIME
, 0, 0, 0, 0);
206 if (srt_getlasterror(NULL
) == SRT_ETIMEOUT
)
207 ret
= AVERROR(EAGAIN
);
209 ret
= libsrt_neterrno(h
);
211 ret
= errlen
? AVERROR(EIO
) : 0;
216 /* TODO de-duplicate code from ff_network_wait_fd_timeout() */
218 static int libsrt_network_wait_fd_timeout(URLContext
*h
, int eid
, int write
, int64_t timeout
, AVIOInterruptCB
*int_cb
)
221 int64_t wait_start
= 0;
224 if (ff_check_interrupt(int_cb
))
226 ret
= libsrt_network_wait_fd(h
, eid
, write
);
227 if (ret
!= AVERROR(EAGAIN
))
231 wait_start
= av_gettime_relative();
232 else if (av_gettime_relative() - wait_start
> timeout
)
233 return AVERROR(ETIMEDOUT
);
238 static int libsrt_listen(int eid
, int fd
, const struct sockaddr
*addr
, socklen_t addrlen
, URLContext
*h
, int64_t timeout
)
242 /* Max streamid length plus an extra space for the terminating null character */
244 int streamid_len
= sizeof(streamid
);
245 if (srt_setsockopt(fd
, SOL_SOCKET
, SRTO_REUSEADDR
, &reuse
, sizeof(reuse
))) {
246 av_log(h
, AV_LOG_WARNING
, "setsockopt(SRTO_REUSEADDR) failed\n");
248 if (srt_bind(fd
, addr
, addrlen
))
249 return libsrt_neterrno(h
);
251 if (srt_listen(fd
, 1))
252 return libsrt_neterrno(h
);
254 ret
= libsrt_network_wait_fd_timeout(h
, eid
, 0, timeout
, &h
->interrupt_callback
);
258 ret
= srt_accept(fd
, NULL
, NULL
);
260 return libsrt_neterrno(h
);
261 if (libsrt_socket_nonblock(ret
, 1) < 0)
262 av_log(h
, AV_LOG_DEBUG
, "libsrt_socket_nonblock failed\n");
263 if (!libsrt_getsockopt(h
, ret
, SRTO_STREAMID
, "SRTO_STREAMID", streamid
, &streamid_len
))
264 /* Note: returned streamid_len doesn't count the terminating null character */
265 av_log(h
, AV_LOG_VERBOSE
, "accept streamid [%s], length %d\n", streamid
, streamid_len
);
270 static int libsrt_listen_connect(int eid
, int fd
, const struct sockaddr
*addr
, socklen_t addrlen
, int64_t timeout
, URLContext
*h
, int will_try_next
)
274 if (srt_connect(fd
, addr
, addrlen
) < 0)
275 return libsrt_neterrno(h
);
277 ret
= libsrt_network_wait_fd_timeout(h
, eid
, 1, timeout
, &h
->interrupt_callback
);
280 av_log(h
, AV_LOG_WARNING
,
281 "Connection to %s failed (%s), trying next address\n",
282 h
->filename
, av_err2str(ret
));
284 av_log(h
, AV_LOG_ERROR
, "Connection to %s failed: %s\n",
285 h
->filename
, av_err2str(ret
));
291 static int libsrt_setsockopt(URLContext
*h
, int fd
, SRT_SOCKOPT optname
, const char * optnamestr
, const void * optval
, int optlen
)
293 if (srt_setsockopt(fd
, 0, optname
, optval
, optlen
) < 0) {
294 av_log(h
, AV_LOG_ERROR
, "failed to set option %s on socket: %s\n", optnamestr
, srt_getlasterror_str());
300 /* - The "POST" options can be altered any time on a connected socket.
301 They MAY have also some meaning when set prior to connecting; such
302 option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
303 Because of that this option is treated special way in this app. */
304 static int libsrt_set_options_post(URLContext
*h
, int fd
)
306 SRTContext
*s
= h
->priv_data
;
308 if ((s
->inputbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_INPUTBW
, "SRTO_INPUTBW", &s
->inputbw
, sizeof(s
->inputbw
)) < 0) ||
309 (s
->oheadbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_OHEADBW
, "SRTO_OHEADBW", &s
->oheadbw
, sizeof(s
->oheadbw
)) < 0)) {
315 /* - The "PRE" options must be set prior to connecting and can't be altered
316 on a connected socket, however if set on a listening socket, they are
317 derived by accept-ed socket. */
318 static int libsrt_set_options_pre(URLContext
*h
, int fd
)
320 SRTContext
*s
= h
->priv_data
;
322 int latency
= s
->latency
/ 1000;
323 int rcvlatency
= s
->rcvlatency
/ 1000;
324 int peerlatency
= s
->peerlatency
/ 1000;
325 #if SRT_VERSION_VALUE >= 0x010302
326 int snddropdelay
= s
->snddropdelay
> 0 ? s
->snddropdelay
/ 1000 : s
->snddropdelay
;
328 int connect_timeout
= s
->connect_timeout
;
330 if ((s
->mode
== SRT_MODE_RENDEZVOUS
&& libsrt_setsockopt(h
, fd
, SRTO_RENDEZVOUS
, "SRTO_RENDEZVOUS", &yes
, sizeof(yes
)) < 0) ||
331 (s
->transtype
!= SRTT_INVALID
&& libsrt_setsockopt(h
, fd
, SRTO_TRANSTYPE
, "SRTO_TRANSTYPE", &s
->transtype
, sizeof(s
->transtype
)) < 0) ||
332 (s
->maxbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MAXBW
, "SRTO_MAXBW", &s
->maxbw
, sizeof(s
->maxbw
)) < 0) ||
333 (s
->pbkeylen
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PBKEYLEN
, "SRTO_PBKEYLEN", &s
->pbkeylen
, sizeof(s
->pbkeylen
)) < 0) ||
334 (s
->passphrase
&& libsrt_setsockopt(h
, fd
, SRTO_PASSPHRASE
, "SRTO_PASSPHRASE", s
->passphrase
, strlen(s
->passphrase
)) < 0) ||
335 #if SRT_VERSION_VALUE >= 0x010302
336 #if SRT_VERSION_VALUE >= 0x010401
337 (s
->enforced_encryption
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_ENFORCEDENCRYPTION
, "SRTO_ENFORCEDENCRYPTION", &s
->enforced_encryption
, sizeof(s
->enforced_encryption
)) < 0) ||
339 /* SRTO_STRICTENC == SRTO_ENFORCEDENCRYPTION (53), but for compatibility, we used SRTO_STRICTENC */
340 (s
->enforced_encryption
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_STRICTENC
, "SRTO_STRICTENC", &s
->enforced_encryption
, sizeof(s
->enforced_encryption
)) < 0) ||
342 (s
->kmrefreshrate
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_KMREFRESHRATE
, "SRTO_KMREFRESHRATE", &s
->kmrefreshrate
, sizeof(s
->kmrefreshrate
)) < 0) ||
343 (s
->kmpreannounce
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_KMPREANNOUNCE
, "SRTO_KMPREANNOUNCE", &s
->kmpreannounce
, sizeof(s
->kmpreannounce
)) < 0) ||
344 (s
->snddropdelay
>=-1 && libsrt_setsockopt(h
, fd
, SRTO_SNDDROPDELAY
, "SRTO_SNDDROPDELAY", &snddropdelay
, sizeof(snddropdelay
)) < 0) ||
346 (s
->mss
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MSS
, "SRTO_MSS", &s
->mss
, sizeof(s
->mss
)) < 0) ||
347 (s
->ffs
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_FC
, "SRTO_FC", &s
->ffs
, sizeof(s
->ffs
)) < 0) ||
348 (s
->ipttl
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_IPTTL
, "SRTO_IPTTL", &s
->ipttl
, sizeof(s
->ipttl
)) < 0) ||
349 (s
->iptos
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_IPTOS
, "SRTO_IPTOS", &s
->iptos
, sizeof(s
->iptos
)) < 0) ||
350 (s
->latency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_LATENCY
, "SRTO_LATENCY", &latency
, sizeof(latency
)) < 0) ||
351 (s
->rcvlatency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_RCVLATENCY
, "SRTO_RCVLATENCY", &rcvlatency
, sizeof(rcvlatency
)) < 0) ||
352 (s
->peerlatency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PEERLATENCY
, "SRTO_PEERLATENCY", &peerlatency
, sizeof(peerlatency
)) < 0) ||
353 (s
->tlpktdrop
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_TLPKTDROP
, "SRTO_TLPKTDROP", &s
->tlpktdrop
, sizeof(s
->tlpktdrop
)) < 0) ||
354 (s
->nakreport
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_NAKREPORT
, "SRTO_NAKREPORT", &s
->nakreport
, sizeof(s
->nakreport
)) < 0) ||
355 (connect_timeout
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_CONNTIMEO
, "SRTO_CONNTIMEO", &connect_timeout
, sizeof(connect_timeout
)) <0 ) ||
356 (s
->sndbuf
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_SNDBUF
, "SRTO_SNDBUF", &s
->sndbuf
, sizeof(s
->sndbuf
)) < 0) ||
357 (s
->rcvbuf
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_RCVBUF
, "SRTO_RCVBUF", &s
->rcvbuf
, sizeof(s
->rcvbuf
)) < 0) ||
358 (s
->lossmaxttl
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_LOSSMAXTTL
, "SRTO_LOSSMAXTTL", &s
->lossmaxttl
, sizeof(s
->lossmaxttl
)) < 0) ||
359 (s
->minversion
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MINVERSION
, "SRTO_MINVERSION", &s
->minversion
, sizeof(s
->minversion
)) < 0) ||
360 (s
->streamid
&& libsrt_setsockopt(h
, fd
, SRTO_STREAMID
, "SRTO_STREAMID", s
->streamid
, strlen(s
->streamid
)) < 0) ||
361 #if SRT_VERSION_VALUE >= 0x010401
362 (s
->smoother
&& libsrt_setsockopt(h
, fd
, SRTO_CONGESTION
, "SRTO_CONGESTION", s
->smoother
, strlen(s
->smoother
)) < 0) ||
364 (s
->smoother
&& libsrt_setsockopt(h
, fd
, SRTO_SMOOTHER
, "SRTO_SMOOTHER", s
->smoother
, strlen(s
->smoother
)) < 0) ||
366 (s
->messageapi
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MESSAGEAPI
, "SRTO_MESSAGEAPI", &s
->messageapi
, sizeof(s
->messageapi
)) < 0) ||
367 (s
->payload_size
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PAYLOADSIZE
, "SRTO_PAYLOADSIZE", &s
->payload_size
, sizeof(s
->payload_size
)) < 0) ||
368 ((h
->flags
& AVIO_FLAG_WRITE
) && libsrt_setsockopt(h
, fd
, SRTO_SENDER
, "SRTO_SENDER", &yes
, sizeof(yes
)) < 0) ||
369 (s
->tsbpd
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_TSBPDMODE
, "SRTO_TSBPDMODE", &s
->tsbpd
, sizeof(s
->tsbpd
)) < 0)) {
373 if (s
->linger
>= 0) {
375 lin
.l_linger
= s
->linger
;
376 lin
.l_onoff
= lin
.l_linger
> 0 ? 1 : 0;
377 if (libsrt_setsockopt(h
, fd
, SRTO_LINGER
, "SRTO_LINGER", &lin
, sizeof(lin
)) < 0)
384 static int libsrt_setup(URLContext
*h
, const char *uri
, int flags
)
386 struct addrinfo hints
= { 0 }, *ai
, *cur_ai
;
388 SRTContext
*s
= h
->priv_data
;
390 char hostname
[1024],proto
[1024],path
[1024];
392 int64_t open_timeout
= 0;
395 av_url_split(proto
, sizeof(proto
), NULL
, 0, hostname
, sizeof(hostname
),
396 &port
, path
, sizeof(path
), uri
);
397 if (strcmp(proto
, "srt"))
398 return AVERROR(EINVAL
);
399 if (port
<= 0 || port
>= 65536) {
400 av_log(h
, AV_LOG_ERROR
, "Port missing in uri\n");
401 return AVERROR(EINVAL
);
403 if (s
->rw_timeout
>= 0) {
404 open_timeout
= h
->rw_timeout
= s
->rw_timeout
;
406 hints
.ai_family
= AF_UNSPEC
;
407 hints
.ai_socktype
= SOCK_DGRAM
;
408 snprintf(portstr
, sizeof(portstr
), "%d", port
);
409 if (s
->mode
== SRT_MODE_LISTENER
)
410 hints
.ai_flags
|= AI_PASSIVE
;
411 ret
= getaddrinfo(hostname
[0] ? hostname
: NULL
, portstr
, &hints
, &ai
);
413 av_log(h
, AV_LOG_ERROR
,
414 "Failed to resolve hostname %s: %s\n",
415 hostname
, gai_strerror(ret
));
423 #if SRT_VERSION_VALUE >= 0x010401
424 fd
= srt_create_socket();
426 fd
= srt_socket(cur_ai
->ai_family
, cur_ai
->ai_socktype
, 0);
429 ret
= libsrt_neterrno(h
);
433 if ((ret
= libsrt_set_options_pre(h
, fd
)) < 0) {
437 /* Set the socket's send or receive buffer sizes, if specified.
438 If unspecified or setting fails, system default is used. */
439 if (s
->recv_buffer_size
> 0) {
440 srt_setsockopt(fd
, SOL_SOCKET
, SRTO_UDP_RCVBUF
, &s
->recv_buffer_size
, sizeof (s
->recv_buffer_size
));
442 if (s
->send_buffer_size
> 0) {
443 srt_setsockopt(fd
, SOL_SOCKET
, SRTO_UDP_SNDBUF
, &s
->send_buffer_size
, sizeof (s
->send_buffer_size
));
445 if (libsrt_socket_nonblock(fd
, 1) < 0)
446 av_log(h
, AV_LOG_DEBUG
, "libsrt_socket_nonblock failed\n");
448 if (s
->mode
== SRT_MODE_LISTENER
) {
449 int read_eid
= ret
= libsrt_epoll_create(h
, fd
, 0);
453 ret
= libsrt_listen(read_eid
, fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
, h
, s
->listen_timeout
);
454 srt_epoll_release(read_eid
);
460 int write_eid
= ret
= libsrt_epoll_create(h
, fd
, 1);
463 if (s
->mode
== SRT_MODE_RENDEZVOUS
) {
464 if (srt_bind(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
)) {
465 ret
= libsrt_neterrno(h
);
466 srt_epoll_release(write_eid
);
471 ret
= libsrt_listen_connect(write_eid
, fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
472 open_timeout
, h
, !!cur_ai
->ai_next
);
473 srt_epoll_release(write_eid
);
475 if (ret
== AVERROR_EXIT
)
481 if ((ret
= libsrt_set_options_post(h
, fd
)) < 0) {
485 if (flags
& AVIO_FLAG_WRITE
) {
487 int optlen
= sizeof(packet_size
);
488 ret
= libsrt_getsockopt(h
, fd
, SRTO_PAYLOADSIZE
, "SRTO_PAYLOADSIZE", &packet_size
, &optlen
);
492 h
->max_packet_size
= packet_size
;
495 ret
= eid
= libsrt_epoll_create(h
, fd
, flags
& AVIO_FLAG_WRITE
);
507 if (cur_ai
->ai_next
) {
508 /* Retry with the next sockaddr */
509 cur_ai
= cur_ai
->ai_next
;
522 static int libsrt_open(URLContext
*h
, const char *uri
, int flags
)
524 SRTContext
*s
= h
->priv_data
;
528 if (srt_startup() < 0) {
529 return AVERROR_UNKNOWN
;
532 /* SRT options (srt/srt.h) */
533 p
= strchr(uri
, '?');
535 ret
= ff_parse_opts_from_query_string(s
, p
, 0);
539 ret
= libsrt_setup(h
, uri
, flags
);
549 static int libsrt_read(URLContext
*h
, uint8_t *buf
, int size
)
551 SRTContext
*s
= h
->priv_data
;
554 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
555 ret
= libsrt_network_wait_fd_timeout(h
, s
->eid
, 0, h
->rw_timeout
, &h
->interrupt_callback
);
560 ret
= srt_recvmsg(s
->fd
, buf
, size
);
562 ret
= libsrt_neterrno(h
);
568 static int libsrt_write(URLContext
*h
, const uint8_t *buf
, int size
)
570 SRTContext
*s
= h
->priv_data
;
573 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
574 ret
= libsrt_network_wait_fd_timeout(h
, s
->eid
, 1, h
->rw_timeout
, &h
->interrupt_callback
);
579 ret
= srt_sendmsg(s
->fd
, buf
, size
, -1, 1);
581 ret
= libsrt_neterrno(h
);
587 static int libsrt_close(URLContext
*h
)
589 SRTContext
*s
= h
->priv_data
;
591 srt_epoll_release(s
->eid
);
599 static const AVClass libsrt_class
= {
600 .class_name
= "libsrt",
601 .item_name
= av_default_item_name
,
602 .option
= libsrt_options
,
603 .version
= LIBAVUTIL_VERSION_INT
,
606 const URLProtocol ff_libsrt_protocol
= {
608 .url_open
= libsrt_open
,
609 .url_read
= libsrt_read
,
610 .url_write
= libsrt_write
,
611 .url_close
= libsrt_close
,
612 .priv_data_size
= sizeof(SRTContext
),
613 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
614 .priv_data_class
= &libsrt_class
,