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/opt.h"
27 #include "libavutil/parseutils.h"
28 #include "libavutil/time.h"
33 #include "os_support.h"
36 /* This is for MPEG-TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE (8 TS packets) */
37 #ifndef SRT_LIVE_DEFAULT_PAYLOAD_SIZE
38 #define SRT_LIVE_DEFAULT_PAYLOAD_SIZE 1316
41 /* This is the maximum payload size for Live mode, should you have a different payload type than MPEG-TS */
42 #ifndef SRT_LIVE_MAX_PAYLOAD_SIZE
43 #define SRT_LIVE_MAX_PAYLOAD_SIZE 1456
48 SRT_MODE_LISTENER
= 1,
49 SRT_MODE_RENDEZVOUS
= 2
52 typedef struct SRTContext
{
57 int64_t listen_timeout
;
64 #if SRT_VERSION_VALUE >= 0x010302
65 int enforced_encryption
;
79 int64_t connect_timeout
;
91 SRT_TRANSTYPE transtype
;
96 #define D AV_OPT_FLAG_DECODING_PARAM
97 #define E AV_OPT_FLAG_ENCODING_PARAM
98 #define OFFSET(x) offsetof(SRTContext, x)
99 static const AVOption libsrt_options
[] = {
100 { "timeout", "Timeout of socket I/O operations (in microseconds)", OFFSET(rw_timeout
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
101 { "listen_timeout", "Connection awaiting timeout (in microseconds)" , OFFSET(listen_timeout
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
102 { "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
},
103 { "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
},
104 { "pkt_size", "Maximum SRT packet size", OFFSET(payload_size
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE
, .flags
= D
|E
, "payload_size" },
105 { "payload_size", "Maximum SRT packet size", OFFSET(payload_size
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE
, .flags
= D
|E
, "payload_size" },
106 { "ts_size", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_LIVE_DEFAULT_PAYLOAD_SIZE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "payload_size" },
107 { "max_size", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_LIVE_MAX_PAYLOAD_SIZE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "payload_size" },
108 { "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
},
109 { "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
},
110 { "passphrase", "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto", OFFSET(passphrase
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
111 #if SRT_VERSION_VALUE >= 0x010302
112 { "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
},
113 { "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
},
114 { "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
},
115 { "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
},
117 { "mss", "The Maximum Segment Size", OFFSET(mss
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 1500, .flags
= D
|E
},
118 { "ffs", "Flight flag size (window size) (in bytes)", OFFSET(ffs
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
119 { "ipttl", "IP Time To Live", OFFSET(ipttl
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 255, .flags
= D
|E
},
120 { "iptos", "IP Type of Service", OFFSET(iptos
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 255, .flags
= D
|E
},
121 { "inputbw", "Estimated input stream rate", OFFSET(inputbw
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
122 { "oheadbw", "MaxBW ceiling based on % over input stream rate", OFFSET(oheadbw
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 100, .flags
= D
|E
},
123 { "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
},
124 { "tsbpddelay", "deprecated, same effect as latency option", OFFSET(latency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
125 { "rcvlatency", "receive latency (in microseconds)", OFFSET(rcvlatency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
126 { "peerlatency", "peer latency (in microseconds)", OFFSET(peerlatency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
127 { "tlpktdrop", "Enable too-late pkt drop", OFFSET(tlpktdrop
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
128 { "nakreport", "Enable receiver to send periodic NAK reports", OFFSET(nakreport
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
129 { "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
},
130 { "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
, "mode" },
131 { "caller", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_CALLER
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "mode" },
132 { "listener", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_LISTENER
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "mode" },
133 { "rendezvous", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_RENDEZVOUS
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "mode" },
134 { "sndbuf", "Send buffer size (in bytes)", OFFSET(sndbuf
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
135 { "rcvbuf", "Receive buffer size (in bytes)", OFFSET(rcvbuf
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
136 { "lossmaxttl", "Maximum possible packet reorder tolerance", OFFSET(lossmaxttl
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
137 { "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
},
138 { "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
},
139 { "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
},
140 { "smoother", "The type of Smoother used for the transmission for that socket", OFFSET(smoother
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
141 { "messageapi", "Enable message API", OFFSET(messageapi
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
142 { "transtype", "The transmission type for the socket", OFFSET(transtype
), AV_OPT_TYPE_INT
, { .i64
= SRTT_INVALID
}, SRTT_LIVE
, SRTT_INVALID
, .flags
= D
|E
, "transtype" },
143 { "live", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRTT_LIVE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "transtype" },
144 { "file", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRTT_FILE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "transtype" },
145 { "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
},
146 { "tsbpd", "Timestamp-based packet delivery", OFFSET(tsbpd
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
150 static int libsrt_neterrno(URLContext
*h
)
153 int err
= srt_getlasterror(&os_errno
);
154 if (err
== SRT_EASYNCRCV
|| err
== SRT_EASYNCSND
)
155 return AVERROR(EAGAIN
);
156 av_log(h
, AV_LOG_ERROR
, "%s\n", srt_getlasterror_str());
157 return os_errno
? AVERROR(os_errno
) : AVERROR_UNKNOWN
;
160 static int libsrt_getsockopt(URLContext
*h
, int fd
, SRT_SOCKOPT optname
, const char * optnamestr
, void * optval
, int * optlen
)
162 if (srt_getsockopt(fd
, 0, optname
, optval
, optlen
) < 0) {
163 av_log(h
, AV_LOG_ERROR
, "failed to get option %s on socket: %s\n", optnamestr
, srt_getlasterror_str());
169 static int libsrt_socket_nonblock(int socket
, int enable
)
171 int ret
, blocking
= enable
? 0 : 1;
172 /* Setting SRTO_{SND,RCV}SYN options to 1 enable blocking mode, setting them to 0 enable non-blocking mode. */
173 ret
= srt_setsockopt(socket
, 0, SRTO_SNDSYN
, &blocking
, sizeof(blocking
));
176 return srt_setsockopt(socket
, 0, SRTO_RCVSYN
, &blocking
, sizeof(blocking
));
179 static int libsrt_epoll_create(URLContext
*h
, int fd
, int write
)
181 int modes
= SRT_EPOLL_ERR
| (write
? SRT_EPOLL_OUT
: SRT_EPOLL_IN
);
182 int eid
= srt_epoll_create();
184 return libsrt_neterrno(h
);
185 if (srt_epoll_add_usock(eid
, fd
, &modes
) < 0) {
186 srt_epoll_release(eid
);
187 return libsrt_neterrno(h
);
192 static int libsrt_network_wait_fd(URLContext
*h
, int eid
, int write
)
194 int ret
, len
= 1, errlen
= 1;
199 ret
= srt_epoll_wait(eid
, error
, &errlen
, ready
, &len
, POLLING_TIME
, 0, 0, 0, 0);
201 ret
= srt_epoll_wait(eid
, ready
, &len
, error
, &errlen
, POLLING_TIME
, 0, 0, 0, 0);
204 if (srt_getlasterror(NULL
) == SRT_ETIMEOUT
)
205 ret
= AVERROR(EAGAIN
);
207 ret
= libsrt_neterrno(h
);
209 ret
= errlen
? AVERROR(EIO
) : 0;
214 /* TODO de-duplicate code from ff_network_wait_fd_timeout() */
216 static int libsrt_network_wait_fd_timeout(URLContext
*h
, int eid
, int write
, int64_t timeout
, AVIOInterruptCB
*int_cb
)
219 int64_t wait_start
= 0;
222 if (ff_check_interrupt(int_cb
))
224 ret
= libsrt_network_wait_fd(h
, eid
, write
);
225 if (ret
!= AVERROR(EAGAIN
))
229 wait_start
= av_gettime_relative();
230 else if (av_gettime_relative() - wait_start
> timeout
)
231 return AVERROR(ETIMEDOUT
);
236 static int libsrt_listen(int eid
, int fd
, const struct sockaddr
*addr
, socklen_t addrlen
, URLContext
*h
, int64_t timeout
)
240 /* Max streamid length plus an extra space for the terminating null character */
242 int streamid_len
= sizeof(streamid
);
243 if (srt_setsockopt(fd
, SOL_SOCKET
, SRTO_REUSEADDR
, &reuse
, sizeof(reuse
))) {
244 av_log(h
, AV_LOG_WARNING
, "setsockopt(SRTO_REUSEADDR) failed\n");
246 if (srt_bind(fd
, addr
, addrlen
))
247 return libsrt_neterrno(h
);
249 if (srt_listen(fd
, 1))
250 return libsrt_neterrno(h
);
252 ret
= libsrt_network_wait_fd_timeout(h
, eid
, 0, timeout
, &h
->interrupt_callback
);
256 ret
= srt_accept(fd
, NULL
, NULL
);
258 return libsrt_neterrno(h
);
259 if (libsrt_socket_nonblock(ret
, 1) < 0)
260 av_log(h
, AV_LOG_DEBUG
, "libsrt_socket_nonblock failed\n");
261 if (!libsrt_getsockopt(h
, ret
, SRTO_STREAMID
, "SRTO_STREAMID", streamid
, &streamid_len
))
262 /* Note: returned streamid_len doesn't count the terminating null character */
263 av_log(h
, AV_LOG_VERBOSE
, "accept streamid [%s], length %d\n", streamid
, streamid_len
);
268 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
)
272 if (srt_connect(fd
, addr
, addrlen
) < 0)
273 return libsrt_neterrno(h
);
275 ret
= libsrt_network_wait_fd_timeout(h
, eid
, 1, timeout
, &h
->interrupt_callback
);
278 av_log(h
, AV_LOG_WARNING
,
279 "Connection to %s failed (%s), trying next address\n",
280 h
->filename
, av_err2str(ret
));
282 av_log(h
, AV_LOG_ERROR
, "Connection to %s failed: %s\n",
283 h
->filename
, av_err2str(ret
));
289 static int libsrt_setsockopt(URLContext
*h
, int fd
, SRT_SOCKOPT optname
, const char * optnamestr
, const void * optval
, int optlen
)
291 if (srt_setsockopt(fd
, 0, optname
, optval
, optlen
) < 0) {
292 av_log(h
, AV_LOG_ERROR
, "failed to set option %s on socket: %s\n", optnamestr
, srt_getlasterror_str());
298 /* - The "POST" options can be altered any time on a connected socket.
299 They MAY have also some meaning when set prior to connecting; such
300 option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
301 Because of that this option is treated special way in this app. */
302 static int libsrt_set_options_post(URLContext
*h
, int fd
)
304 SRTContext
*s
= h
->priv_data
;
306 if ((s
->inputbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_INPUTBW
, "SRTO_INPUTBW", &s
->inputbw
, sizeof(s
->inputbw
)) < 0) ||
307 (s
->oheadbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_OHEADBW
, "SRTO_OHEADBW", &s
->oheadbw
, sizeof(s
->oheadbw
)) < 0)) {
313 /* - The "PRE" options must be set prior to connecting and can't be altered
314 on a connected socket, however if set on a listening socket, they are
315 derived by accept-ed socket. */
316 static int libsrt_set_options_pre(URLContext
*h
, int fd
)
318 SRTContext
*s
= h
->priv_data
;
320 int latency
= s
->latency
/ 1000;
321 int rcvlatency
= s
->rcvlatency
/ 1000;
322 int peerlatency
= s
->peerlatency
/ 1000;
323 #if SRT_VERSION_VALUE >= 0x010302
324 int snddropdelay
= s
->snddropdelay
> 0 ? s
->snddropdelay
/ 1000 : s
->snddropdelay
;
326 int connect_timeout
= s
->connect_timeout
;
328 if ((s
->mode
== SRT_MODE_RENDEZVOUS
&& libsrt_setsockopt(h
, fd
, SRTO_RENDEZVOUS
, "SRTO_RENDEZVOUS", &yes
, sizeof(yes
)) < 0) ||
329 (s
->transtype
!= SRTT_INVALID
&& libsrt_setsockopt(h
, fd
, SRTO_TRANSTYPE
, "SRTO_TRANSTYPE", &s
->transtype
, sizeof(s
->transtype
)) < 0) ||
330 (s
->maxbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MAXBW
, "SRTO_MAXBW", &s
->maxbw
, sizeof(s
->maxbw
)) < 0) ||
331 (s
->pbkeylen
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PBKEYLEN
, "SRTO_PBKEYLEN", &s
->pbkeylen
, sizeof(s
->pbkeylen
)) < 0) ||
332 (s
->passphrase
&& libsrt_setsockopt(h
, fd
, SRTO_PASSPHRASE
, "SRTO_PASSPHRASE", s
->passphrase
, strlen(s
->passphrase
)) < 0) ||
333 #if SRT_VERSION_VALUE >= 0x010302
334 #if SRT_VERSION_VALUE >= 0x010401
335 (s
->enforced_encryption
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_ENFORCEDENCRYPTION
, "SRTO_ENFORCEDENCRYPTION", &s
->enforced_encryption
, sizeof(s
->enforced_encryption
)) < 0) ||
337 /* SRTO_STRICTENC == SRTO_ENFORCEDENCRYPTION (53), but for compatibility, we used SRTO_STRICTENC */
338 (s
->enforced_encryption
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_STRICTENC
, "SRTO_STRICTENC", &s
->enforced_encryption
, sizeof(s
->enforced_encryption
)) < 0) ||
340 (s
->kmrefreshrate
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_KMREFRESHRATE
, "SRTO_KMREFRESHRATE", &s
->kmrefreshrate
, sizeof(s
->kmrefreshrate
)) < 0) ||
341 (s
->kmpreannounce
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_KMPREANNOUNCE
, "SRTO_KMPREANNOUNCE", &s
->kmpreannounce
, sizeof(s
->kmpreannounce
)) < 0) ||
342 (s
->snddropdelay
>=-1 && libsrt_setsockopt(h
, fd
, SRTO_SNDDROPDELAY
, "SRTO_SNDDROPDELAY", &snddropdelay
, sizeof(snddropdelay
)) < 0) ||
344 (s
->mss
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MSS
, "SRTO_MSS", &s
->mss
, sizeof(s
->mss
)) < 0) ||
345 (s
->ffs
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_FC
, "SRTO_FC", &s
->ffs
, sizeof(s
->ffs
)) < 0) ||
346 (s
->ipttl
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_IPTTL
, "SRTO_IPTTL", &s
->ipttl
, sizeof(s
->ipttl
)) < 0) ||
347 (s
->iptos
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_IPTOS
, "SRTO_IPTOS", &s
->iptos
, sizeof(s
->iptos
)) < 0) ||
348 (s
->latency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_LATENCY
, "SRTO_LATENCY", &latency
, sizeof(latency
)) < 0) ||
349 (s
->rcvlatency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_RCVLATENCY
, "SRTO_RCVLATENCY", &rcvlatency
, sizeof(rcvlatency
)) < 0) ||
350 (s
->peerlatency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PEERLATENCY
, "SRTO_PEERLATENCY", &peerlatency
, sizeof(peerlatency
)) < 0) ||
351 (s
->tlpktdrop
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_TLPKTDROP
, "SRTO_TLPKTDROP", &s
->tlpktdrop
, sizeof(s
->tlpktdrop
)) < 0) ||
352 (s
->nakreport
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_NAKREPORT
, "SRTO_NAKREPORT", &s
->nakreport
, sizeof(s
->nakreport
)) < 0) ||
353 (connect_timeout
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_CONNTIMEO
, "SRTO_CONNTIMEO", &connect_timeout
, sizeof(connect_timeout
)) <0 ) ||
354 (s
->sndbuf
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_SNDBUF
, "SRTO_SNDBUF", &s
->sndbuf
, sizeof(s
->sndbuf
)) < 0) ||
355 (s
->rcvbuf
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_RCVBUF
, "SRTO_RCVBUF", &s
->rcvbuf
, sizeof(s
->rcvbuf
)) < 0) ||
356 (s
->lossmaxttl
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_LOSSMAXTTL
, "SRTO_LOSSMAXTTL", &s
->lossmaxttl
, sizeof(s
->lossmaxttl
)) < 0) ||
357 (s
->minversion
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MINVERSION
, "SRTO_MINVERSION", &s
->minversion
, sizeof(s
->minversion
)) < 0) ||
358 (s
->streamid
&& libsrt_setsockopt(h
, fd
, SRTO_STREAMID
, "SRTO_STREAMID", s
->streamid
, strlen(s
->streamid
)) < 0) ||
359 #if SRT_VERSION_VALUE >= 0x010401
360 (s
->smoother
&& libsrt_setsockopt(h
, fd
, SRTO_CONGESTION
, "SRTO_CONGESTION", s
->smoother
, strlen(s
->smoother
)) < 0) ||
362 (s
->smoother
&& libsrt_setsockopt(h
, fd
, SRTO_SMOOTHER
, "SRTO_SMOOTHER", s
->smoother
, strlen(s
->smoother
)) < 0) ||
364 (s
->messageapi
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MESSAGEAPI
, "SRTO_MESSAGEAPI", &s
->messageapi
, sizeof(s
->messageapi
)) < 0) ||
365 (s
->payload_size
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PAYLOADSIZE
, "SRTO_PAYLOADSIZE", &s
->payload_size
, sizeof(s
->payload_size
)) < 0) ||
366 ((h
->flags
& AVIO_FLAG_WRITE
) && libsrt_setsockopt(h
, fd
, SRTO_SENDER
, "SRTO_SENDER", &yes
, sizeof(yes
)) < 0) ||
367 (s
->tsbpd
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_TSBPDMODE
, "SRTO_TSBPDMODE", &s
->tsbpd
, sizeof(s
->tsbpd
)) < 0)) {
371 if (s
->linger
>= 0) {
373 lin
.l_linger
= s
->linger
;
374 lin
.l_onoff
= lin
.l_linger
> 0 ? 1 : 0;
375 if (libsrt_setsockopt(h
, fd
, SRTO_LINGER
, "SRTO_LINGER", &lin
, sizeof(lin
)) < 0)
382 static int libsrt_setup(URLContext
*h
, const char *uri
, int flags
)
384 struct addrinfo hints
= { 0 }, *ai
, *cur_ai
;
386 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 p
= strchr(uri
, '?');
405 if (av_find_info_tag(buf
, sizeof(buf
), "timeout", p
)) {
406 s
->rw_timeout
= strtoll(buf
, NULL
, 10);
408 if (av_find_info_tag(buf
, sizeof(buf
), "listen_timeout", p
)) {
409 s
->listen_timeout
= strtoll(buf
, NULL
, 10);
412 if (s
->rw_timeout
>= 0) {
413 open_timeout
= h
->rw_timeout
= s
->rw_timeout
;
415 hints
.ai_family
= AF_UNSPEC
;
416 hints
.ai_socktype
= SOCK_DGRAM
;
417 snprintf(portstr
, sizeof(portstr
), "%d", port
);
418 if (s
->mode
== SRT_MODE_LISTENER
)
419 hints
.ai_flags
|= AI_PASSIVE
;
420 ret
= getaddrinfo(hostname
[0] ? hostname
: NULL
, portstr
, &hints
, &ai
);
422 av_log(h
, AV_LOG_ERROR
,
423 "Failed to resolve hostname %s: %s\n",
424 hostname
, gai_strerror(ret
));
432 #if SRT_VERSION_VALUE >= 0x010401
433 fd
= srt_create_socket();
435 fd
= srt_socket(cur_ai
->ai_family
, cur_ai
->ai_socktype
, 0);
438 ret
= libsrt_neterrno(h
);
442 if ((ret
= libsrt_set_options_pre(h
, fd
)) < 0) {
446 /* Set the socket's send or receive buffer sizes, if specified.
447 If unspecified or setting fails, system default is used. */
448 if (s
->recv_buffer_size
> 0) {
449 srt_setsockopt(fd
, SOL_SOCKET
, SRTO_UDP_RCVBUF
, &s
->recv_buffer_size
, sizeof (s
->recv_buffer_size
));
451 if (s
->send_buffer_size
> 0) {
452 srt_setsockopt(fd
, SOL_SOCKET
, SRTO_UDP_SNDBUF
, &s
->send_buffer_size
, sizeof (s
->send_buffer_size
));
454 if (libsrt_socket_nonblock(fd
, 1) < 0)
455 av_log(h
, AV_LOG_DEBUG
, "libsrt_socket_nonblock failed\n");
457 if (s
->mode
== SRT_MODE_LISTENER
) {
458 int read_eid
= ret
= libsrt_epoll_create(h
, fd
, 0);
462 ret
= libsrt_listen(read_eid
, fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
, h
, s
->listen_timeout
);
463 srt_epoll_release(read_eid
);
469 int write_eid
= ret
= libsrt_epoll_create(h
, fd
, 1);
472 if (s
->mode
== SRT_MODE_RENDEZVOUS
) {
473 if (srt_bind(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
)) {
474 ret
= libsrt_neterrno(h
);
475 srt_epoll_release(write_eid
);
480 ret
= libsrt_listen_connect(write_eid
, fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
481 open_timeout
, h
, !!cur_ai
->ai_next
);
482 srt_epoll_release(write_eid
);
484 if (ret
== AVERROR_EXIT
)
490 if ((ret
= libsrt_set_options_post(h
, fd
)) < 0) {
494 if (flags
& AVIO_FLAG_WRITE
) {
496 int optlen
= sizeof(packet_size
);
497 ret
= libsrt_getsockopt(h
, fd
, SRTO_PAYLOADSIZE
, "SRTO_PAYLOADSIZE", &packet_size
, &optlen
);
501 h
->max_packet_size
= packet_size
;
504 ret
= eid
= libsrt_epoll_create(h
, fd
, flags
& AVIO_FLAG_WRITE
);
516 if (cur_ai
->ai_next
) {
517 /* Retry with the next sockaddr */
518 cur_ai
= cur_ai
->ai_next
;
531 static int libsrt_open(URLContext
*h
, const char *uri
, int flags
)
533 SRTContext
*s
= h
->priv_data
;
538 if (srt_startup() < 0) {
539 return AVERROR_UNKNOWN
;
542 /* SRT options (srt/srt.h) */
543 p
= strchr(uri
, '?');
545 if (av_find_info_tag(buf
, sizeof(buf
), "maxbw", p
)) {
546 s
->maxbw
= strtoll(buf
, NULL
, 10);
548 if (av_find_info_tag(buf
, sizeof(buf
), "pbkeylen", p
)) {
549 s
->pbkeylen
= strtol(buf
, NULL
, 10);
551 if (av_find_info_tag(buf
, sizeof(buf
), "passphrase", p
)) {
552 av_freep(&s
->passphrase
);
553 s
->passphrase
= av_strndup(buf
, strlen(buf
));
555 #if SRT_VERSION_VALUE >= 0x010302
556 if (av_find_info_tag(buf
, sizeof(buf
), "enforced_encryption", p
)) {
557 s
->enforced_encryption
= strtol(buf
, NULL
, 10);
559 if (av_find_info_tag(buf
, sizeof(buf
), "kmrefreshrate", p
)) {
560 s
->kmrefreshrate
= strtol(buf
, NULL
, 10);
562 if (av_find_info_tag(buf
, sizeof(buf
), "kmpreannounce", p
)) {
563 s
->kmpreannounce
= strtol(buf
, NULL
, 10);
565 if (av_find_info_tag(buf
, sizeof(buf
), "snddropdelay", p
)) {
566 s
->snddropdelay
= strtoll(buf
, NULL
, 10);
569 if (av_find_info_tag(buf
, sizeof(buf
), "mss", p
)) {
570 s
->mss
= strtol(buf
, NULL
, 10);
572 if (av_find_info_tag(buf
, sizeof(buf
), "ffs", p
)) {
573 s
->ffs
= strtol(buf
, NULL
, 10);
575 if (av_find_info_tag(buf
, sizeof(buf
), "ipttl", p
)) {
576 s
->ipttl
= strtol(buf
, NULL
, 10);
578 if (av_find_info_tag(buf
, sizeof(buf
), "iptos", p
)) {
579 s
->iptos
= strtol(buf
, NULL
, 10);
581 if (av_find_info_tag(buf
, sizeof(buf
), "inputbw", p
)) {
582 s
->inputbw
= strtoll(buf
, NULL
, 10);
584 if (av_find_info_tag(buf
, sizeof(buf
), "oheadbw", p
)) {
585 s
->oheadbw
= strtol(buf
, NULL
, 10);
587 if (av_find_info_tag(buf
, sizeof(buf
), "latency", p
)) {
588 s
->latency
= strtoll(buf
, NULL
, 10);
590 if (av_find_info_tag(buf
, sizeof(buf
), "tsbpddelay", p
)) {
591 s
->latency
= strtoll(buf
, NULL
, 10);
593 if (av_find_info_tag(buf
, sizeof(buf
), "rcvlatency", p
)) {
594 s
->rcvlatency
= strtoll(buf
, NULL
, 10);
596 if (av_find_info_tag(buf
, sizeof(buf
), "peerlatency", p
)) {
597 s
->peerlatency
= strtoll(buf
, NULL
, 10);
599 if (av_find_info_tag(buf
, sizeof(buf
), "tlpktdrop", p
)) {
600 s
->tlpktdrop
= strtol(buf
, NULL
, 10);
602 if (av_find_info_tag(buf
, sizeof(buf
), "nakreport", p
)) {
603 s
->nakreport
= strtol(buf
, NULL
, 10);
605 if (av_find_info_tag(buf
, sizeof(buf
), "connect_timeout", p
)) {
606 s
->connect_timeout
= strtoll(buf
, NULL
, 10);
608 if (av_find_info_tag(buf
, sizeof(buf
), "payload_size", p
) ||
609 av_find_info_tag(buf
, sizeof(buf
), "pkt_size", p
)) {
610 s
->payload_size
= strtol(buf
, NULL
, 10);
612 if (av_find_info_tag(buf
, sizeof(buf
), "mode", p
)) {
613 if (!strcmp(buf
, "caller")) {
614 s
->mode
= SRT_MODE_CALLER
;
615 } else if (!strcmp(buf
, "listener")) {
616 s
->mode
= SRT_MODE_LISTENER
;
617 } else if (!strcmp(buf
, "rendezvous")) {
618 s
->mode
= SRT_MODE_RENDEZVOUS
;
620 ret
= AVERROR(EINVAL
);
624 if (av_find_info_tag(buf
, sizeof(buf
), "sndbuf", p
)) {
625 s
->sndbuf
= strtol(buf
, NULL
, 10);
627 if (av_find_info_tag(buf
, sizeof(buf
), "rcvbuf", p
)) {
628 s
->rcvbuf
= strtol(buf
, NULL
, 10);
630 if (av_find_info_tag(buf
, sizeof(buf
), "lossmaxttl", p
)) {
631 s
->lossmaxttl
= strtol(buf
, NULL
, 10);
633 if (av_find_info_tag(buf
, sizeof(buf
), "minversion", p
)) {
634 s
->minversion
= strtol(buf
, NULL
, 0);
636 if (av_find_info_tag(buf
, sizeof(buf
), "streamid", p
)) {
637 av_freep(&s
->streamid
);
638 s
->streamid
= av_strdup(buf
);
640 ret
= AVERROR(ENOMEM
);
644 if (av_find_info_tag(buf
, sizeof(buf
), "smoother", p
)) {
645 av_freep(&s
->smoother
);
646 s
->smoother
= av_strdup(buf
);
648 ret
= AVERROR(ENOMEM
);
652 if (av_find_info_tag(buf
, sizeof(buf
), "messageapi", p
)) {
653 s
->messageapi
= strtol(buf
, NULL
, 10);
655 if (av_find_info_tag(buf
, sizeof(buf
), "transtype", p
)) {
656 if (!strcmp(buf
, "live")) {
657 s
->transtype
= SRTT_LIVE
;
658 } else if (!strcmp(buf
, "file")) {
659 s
->transtype
= SRTT_FILE
;
661 ret
= AVERROR(EINVAL
);
665 if (av_find_info_tag(buf
, sizeof(buf
), "linger", p
)) {
666 s
->linger
= strtol(buf
, NULL
, 10);
669 ret
= libsrt_setup(h
, uri
, flags
);
675 av_freep(&s
->smoother
);
676 av_freep(&s
->streamid
);
681 static int libsrt_read(URLContext
*h
, uint8_t *buf
, int size
)
683 SRTContext
*s
= h
->priv_data
;
686 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
687 ret
= libsrt_network_wait_fd_timeout(h
, s
->eid
, 0, h
->rw_timeout
, &h
->interrupt_callback
);
692 ret
= srt_recvmsg(s
->fd
, buf
, size
);
694 ret
= libsrt_neterrno(h
);
700 static int libsrt_write(URLContext
*h
, const uint8_t *buf
, int size
)
702 SRTContext
*s
= h
->priv_data
;
705 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
706 ret
= libsrt_network_wait_fd_timeout(h
, s
->eid
, 1, h
->rw_timeout
, &h
->interrupt_callback
);
711 ret
= srt_sendmsg(s
->fd
, buf
, size
, -1, 1);
713 ret
= libsrt_neterrno(h
);
719 static int libsrt_close(URLContext
*h
)
721 SRTContext
*s
= h
->priv_data
;
723 srt_epoll_release(s
->eid
);
731 static const AVClass libsrt_class
= {
732 .class_name
= "libsrt",
733 .item_name
= av_default_item_name
,
734 .option
= libsrt_options
,
735 .version
= LIBAVUTIL_VERSION_INT
,
738 const URLProtocol ff_libsrt_protocol
= {
740 .url_open
= libsrt_open
,
741 .url_read
= libsrt_read
,
742 .url_write
= libsrt_write
,
743 .url_close
= libsrt_close
,
744 .priv_data_size
= sizeof(SRTContext
),
745 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
746 .priv_data_class
= &libsrt_class
,