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/avassert.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/parseutils.h"
29 #include "libavutil/time.h"
34 #include "os_support.h"
37 /* This is for MPEG-TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE (8 TS packets) */
38 #ifndef SRT_LIVE_DEFAULT_PAYLOAD_SIZE
39 #define SRT_LIVE_DEFAULT_PAYLOAD_SIZE 1316
42 /* This is the maximum payload size for Live mode, should you have a different payload type than MPEG-TS */
43 #ifndef SRT_LIVE_MAX_PAYLOAD_SIZE
44 #define SRT_LIVE_MAX_PAYLOAD_SIZE 1456
49 SRT_MODE_LISTENER
= 1,
50 SRT_MODE_RENDEZVOUS
= 2
53 typedef struct SRTContext
{
59 int64_t listen_timeout
;
66 #if SRT_VERSION_VALUE >= 0x010302
67 int enforced_encryption
;
80 int64_t connect_timeout
;
92 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
},
116 { "mss", "The Maximum Segment Size", OFFSET(mss
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 1500, .flags
= D
|E
},
117 { "ffs", "Flight flag size (window size) (in bytes)", OFFSET(ffs
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
118 { "ipttl", "IP Time To Live", OFFSET(ipttl
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 255, .flags
= D
|E
},
119 { "iptos", "IP Type of Service", OFFSET(iptos
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 255, .flags
= D
|E
},
120 { "inputbw", "Estimated input stream rate", OFFSET(inputbw
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
121 { "oheadbw", "MaxBW ceiling based on % over input stream rate", OFFSET(oheadbw
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, 100, .flags
= D
|E
},
122 { "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
},
123 { "tsbpddelay", "deprecated, same effect as latency option", OFFSET(latency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
124 { "rcvlatency", "receive latency (in microseconds)", OFFSET(rcvlatency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
125 { "peerlatency", "peer latency (in microseconds)", OFFSET(peerlatency
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, .flags
= D
|E
},
126 { "tlpktdrop", "Enable receiver pkt drop", OFFSET(tlpktdrop
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
127 { "nakreport", "Enable receiver to send periodic NAK reports", OFFSET(nakreport
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
128 { "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
},
129 { "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" },
130 { "caller", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_CALLER
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "mode" },
131 { "listener", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_LISTENER
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "mode" },
132 { "rendezvous", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRT_MODE_RENDEZVOUS
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "mode" },
133 { "sndbuf", "Send buffer size (in bytes)", OFFSET(sndbuf
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
134 { "rcvbuf", "Receive buffer size (in bytes)", OFFSET(rcvbuf
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
135 { "lossmaxttl", "Maximum possible packet reorder tolerance", OFFSET(lossmaxttl
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, INT_MAX
, .flags
= D
|E
},
136 { "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
},
137 { "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
},
138 { "smoother", "The type of Smoother used for the transmission for that socket", OFFSET(smoother
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= D
|E
},
139 { "messageapi", "Enable message API", OFFSET(messageapi
), AV_OPT_TYPE_BOOL
, { .i64
= -1 }, -1, 1, .flags
= D
|E
},
140 { "transtype", "The transmission type for the socket", OFFSET(transtype
), AV_OPT_TYPE_INT
, { .i64
= SRTT_INVALID
}, SRTT_LIVE
, SRTT_INVALID
, .flags
= D
|E
, "transtype" },
141 { "live", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRTT_LIVE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "transtype" },
142 { "file", NULL
, 0, AV_OPT_TYPE_CONST
, { .i64
= SRTT_FILE
}, INT_MIN
, INT_MAX
, .flags
= D
|E
, "transtype" },
143 { "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
},
147 static int libsrt_neterrno(URLContext
*h
)
150 int err
= srt_getlasterror(&os_errno
);
151 if (err
== SRT_EASYNCRCV
|| err
== SRT_EASYNCSND
)
152 return AVERROR(EAGAIN
);
153 av_log(h
, AV_LOG_ERROR
, "%s\n", srt_getlasterror_str());
154 return os_errno
? AVERROR(os_errno
) : AVERROR_UNKNOWN
;
157 static int libsrt_socket_nonblock(int socket
, int enable
)
159 int ret
, blocking
= enable
? 0 : 1;
160 /* Setting SRTO_{SND,RCV}SYN options to 1 enable blocking mode, setting them to 0 enable non-blocking mode. */
161 ret
= srt_setsockopt(socket
, 0, SRTO_SNDSYN
, &blocking
, sizeof(blocking
));
164 return srt_setsockopt(socket
, 0, SRTO_RCVSYN
, &blocking
, sizeof(blocking
));
167 static int libsrt_network_wait_fd(URLContext
*h
, int eid
, int fd
, int write
)
169 int ret
, len
= 1, errlen
= 1;
170 int modes
= SRT_EPOLL_ERR
| (write
? SRT_EPOLL_OUT
: SRT_EPOLL_IN
);
174 if (srt_epoll_add_usock(eid
, fd
, &modes
) < 0)
175 return libsrt_neterrno(h
);
177 ret
= srt_epoll_wait(eid
, error
, &errlen
, ready
, &len
, POLLING_TIME
, 0, 0, 0, 0);
179 ret
= srt_epoll_wait(eid
, ready
, &len
, error
, &errlen
, POLLING_TIME
, 0, 0, 0, 0);
182 if (srt_getlasterror(NULL
) == SRT_ETIMEOUT
)
183 ret
= AVERROR(EAGAIN
);
185 ret
= libsrt_neterrno(h
);
187 ret
= errlen
? AVERROR(EIO
) : 0;
189 if (srt_epoll_remove_usock(eid
, fd
) < 0)
190 return libsrt_neterrno(h
);
194 /* TODO de-duplicate code from ff_network_wait_fd_timeout() */
196 static int libsrt_network_wait_fd_timeout(URLContext
*h
, int eid
, int fd
, int write
, int64_t timeout
, AVIOInterruptCB
*int_cb
)
199 int64_t wait_start
= 0;
202 if (ff_check_interrupt(int_cb
))
204 ret
= libsrt_network_wait_fd(h
, eid
, fd
, write
);
205 if (ret
!= AVERROR(EAGAIN
))
209 wait_start
= av_gettime_relative();
210 else if (av_gettime_relative() - wait_start
> timeout
)
211 return AVERROR(ETIMEDOUT
);
216 static int libsrt_listen(int eid
, int fd
, const struct sockaddr
*addr
, socklen_t addrlen
, URLContext
*h
, int64_t timeout
)
220 if (srt_setsockopt(fd
, SOL_SOCKET
, SRTO_REUSEADDR
, &reuse
, sizeof(reuse
))) {
221 av_log(h
, AV_LOG_WARNING
, "setsockopt(SRTO_REUSEADDR) failed\n");
223 ret
= srt_bind(fd
, addr
, addrlen
);
225 return libsrt_neterrno(h
);
227 ret
= srt_listen(fd
, 1);
229 return libsrt_neterrno(h
);
231 ret
= libsrt_network_wait_fd_timeout(h
, eid
, fd
, 1, timeout
, &h
->interrupt_callback
);
235 ret
= srt_accept(fd
, NULL
, NULL
);
237 return libsrt_neterrno(h
);
238 if (libsrt_socket_nonblock(ret
, 1) < 0)
239 av_log(h
, AV_LOG_DEBUG
, "libsrt_socket_nonblock failed\n");
244 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
)
248 ret
= srt_connect(fd
, addr
, addrlen
);
250 return libsrt_neterrno(h
);
252 ret
= libsrt_network_wait_fd_timeout(h
, eid
, fd
, 1, timeout
, &h
->interrupt_callback
);
255 av_log(h
, AV_LOG_WARNING
,
256 "Connection to %s failed (%s), trying next address\n",
257 h
->filename
, av_err2str(ret
));
259 av_log(h
, AV_LOG_ERROR
, "Connection to %s failed: %s\n",
260 h
->filename
, av_err2str(ret
));
266 static int libsrt_setsockopt(URLContext
*h
, int fd
, SRT_SOCKOPT optname
, const char * optnamestr
, const void * optval
, int optlen
)
268 if (srt_setsockopt(fd
, 0, optname
, optval
, optlen
) < 0) {
269 av_log(h
, AV_LOG_ERROR
, "failed to set option %s on socket: %s\n", optnamestr
, srt_getlasterror_str());
275 static int libsrt_getsockopt(URLContext
*h
, int fd
, SRT_SOCKOPT optname
, const char * optnamestr
, void * optval
, int * optlen
)
277 if (srt_getsockopt(fd
, 0, optname
, optval
, optlen
) < 0) {
278 av_log(h
, AV_LOG_ERROR
, "failed to get option %s on socket: %s\n", optnamestr
, srt_getlasterror_str());
284 /* - The "POST" options can be altered any time on a connected socket.
285 They MAY have also some meaning when set prior to connecting; such
286 option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
287 Because of that this option is treated special way in this app. */
288 static int libsrt_set_options_post(URLContext
*h
, int fd
)
290 SRTContext
*s
= h
->priv_data
;
292 if ((s
->inputbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_INPUTBW
, "SRTO_INPUTBW", &s
->inputbw
, sizeof(s
->inputbw
)) < 0) ||
293 (s
->oheadbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_OHEADBW
, "SRTO_OHEADBW", &s
->oheadbw
, sizeof(s
->oheadbw
)) < 0)) {
299 /* - The "PRE" options must be set prior to connecting and can't be altered
300 on a connected socket, however if set on a listening socket, they are
301 derived by accept-ed socket. */
302 static int libsrt_set_options_pre(URLContext
*h
, int fd
)
304 SRTContext
*s
= h
->priv_data
;
306 int latency
= s
->latency
/ 1000;
307 int rcvlatency
= s
->rcvlatency
/ 1000;
308 int peerlatency
= s
->peerlatency
/ 1000;
309 int connect_timeout
= s
->connect_timeout
;
311 if ((s
->mode
== SRT_MODE_RENDEZVOUS
&& libsrt_setsockopt(h
, fd
, SRTO_RENDEZVOUS
, "SRTO_RENDEZVOUS", &yes
, sizeof(yes
)) < 0) ||
312 (s
->transtype
!= SRTT_INVALID
&& libsrt_setsockopt(h
, fd
, SRTO_TRANSTYPE
, "SRTO_TRANSTYPE", &s
->transtype
, sizeof(s
->transtype
)) < 0) ||
313 (s
->maxbw
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MAXBW
, "SRTO_MAXBW", &s
->maxbw
, sizeof(s
->maxbw
)) < 0) ||
314 (s
->pbkeylen
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PBKEYLEN
, "SRTO_PBKEYLEN", &s
->pbkeylen
, sizeof(s
->pbkeylen
)) < 0) ||
315 (s
->passphrase
&& libsrt_setsockopt(h
, fd
, SRTO_PASSPHRASE
, "SRTO_PASSPHRASE", s
->passphrase
, strlen(s
->passphrase
)) < 0) ||
316 #if SRT_VERSION_VALUE >= 0x010302
317 #if SRT_VERSION_VALUE >= 0x010401
318 (s
->enforced_encryption
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_ENFORCEDENCRYPTION
, "SRTO_ENFORCEDENCRYPTION", &s
->enforced_encryption
, sizeof(s
->enforced_encryption
)) < 0) ||
320 /* SRTO_STRICTENC == SRTO_ENFORCEDENCRYPTION (53), but for compatibility, we used SRTO_STRICTENC */
321 (s
->enforced_encryption
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_STRICTENC
, "SRTO_STRICTENC", &s
->enforced_encryption
, sizeof(s
->enforced_encryption
)) < 0) ||
323 (s
->kmrefreshrate
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_KMREFRESHRATE
, "SRTO_KMREFRESHRATE", &s
->kmrefreshrate
, sizeof(s
->kmrefreshrate
)) < 0) ||
324 (s
->kmpreannounce
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_KMPREANNOUNCE
, "SRTO_KMPREANNOUNCE", &s
->kmpreannounce
, sizeof(s
->kmpreannounce
)) < 0) ||
326 (s
->mss
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MSS
, "SRTO_MSS", &s
->mss
, sizeof(s
->mss
)) < 0) ||
327 (s
->ffs
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_FC
, "SRTO_FC", &s
->ffs
, sizeof(s
->ffs
)) < 0) ||
328 (s
->ipttl
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_IPTTL
, "SRTO_IPTTL", &s
->ipttl
, sizeof(s
->ipttl
)) < 0) ||
329 (s
->iptos
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_IPTOS
, "SRTO_IPTOS", &s
->iptos
, sizeof(s
->iptos
)) < 0) ||
330 (s
->latency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_LATENCY
, "SRTO_LATENCY", &latency
, sizeof(latency
)) < 0) ||
331 (s
->rcvlatency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_RCVLATENCY
, "SRTO_RCVLATENCY", &rcvlatency
, sizeof(rcvlatency
)) < 0) ||
332 (s
->peerlatency
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PEERLATENCY
, "SRTO_PEERLATENCY", &peerlatency
, sizeof(peerlatency
)) < 0) ||
333 (s
->tlpktdrop
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_TLPKTDROP
, "SRTO_TLPKDROP", &s
->tlpktdrop
, sizeof(s
->tlpktdrop
)) < 0) ||
334 (s
->nakreport
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_NAKREPORT
, "SRTO_NAKREPORT", &s
->nakreport
, sizeof(s
->nakreport
)) < 0) ||
335 (connect_timeout
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_CONNTIMEO
, "SRTO_CONNTIMEO", &connect_timeout
, sizeof(connect_timeout
)) <0 ) ||
336 (s
->sndbuf
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_SNDBUF
, "SRTO_SNDBUF", &s
->sndbuf
, sizeof(s
->sndbuf
)) < 0) ||
337 (s
->rcvbuf
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_RCVBUF
, "SRTO_RCVBUF", &s
->rcvbuf
, sizeof(s
->rcvbuf
)) < 0) ||
338 (s
->lossmaxttl
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_LOSSMAXTTL
, "SRTO_LOSSMAXTTL", &s
->lossmaxttl
, sizeof(s
->lossmaxttl
)) < 0) ||
339 (s
->minversion
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MINVERSION
, "SRTO_MINVERSION", &s
->minversion
, sizeof(s
->minversion
)) < 0) ||
340 (s
->streamid
&& libsrt_setsockopt(h
, fd
, SRTO_STREAMID
, "SRTO_STREAMID", s
->streamid
, strlen(s
->streamid
)) < 0) ||
341 #if SRT_VERSION_VALUE >= 0x010401
342 (s
->smoother
&& libsrt_setsockopt(h
, fd
, SRTO_CONGESTION
, "SRTO_CONGESTION", s
->smoother
, strlen(s
->smoother
)) < 0) ||
344 (s
->smoother
&& libsrt_setsockopt(h
, fd
, SRTO_SMOOTHER
, "SRTO_SMOOTHER", s
->smoother
, strlen(s
->smoother
)) < 0) ||
346 (s
->messageapi
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_MESSAGEAPI
, "SRTO_MESSAGEAPI", &s
->messageapi
, sizeof(s
->messageapi
)) < 0) ||
347 (s
->payload_size
>= 0 && libsrt_setsockopt(h
, fd
, SRTO_PAYLOADSIZE
, "SRTO_PAYLOADSIZE", &s
->payload_size
, sizeof(s
->payload_size
)) < 0) ||
348 ((h
->flags
& AVIO_FLAG_WRITE
) && libsrt_setsockopt(h
, fd
, SRTO_SENDER
, "SRTO_SENDER", &yes
, sizeof(yes
)) < 0)) {
352 if (s
->linger
>= 0) {
354 lin
.l_linger
= s
->linger
;
355 lin
.l_onoff
= lin
.l_linger
> 0 ? 1 : 0;
356 if (libsrt_setsockopt(h
, fd
, SRTO_LINGER
, "SRTO_LINGER", &lin
, sizeof(lin
)) < 0)
363 static int libsrt_setup(URLContext
*h
, const char *uri
, int flags
)
365 struct addrinfo hints
= { 0 }, *ai
, *cur_ai
;
366 int port
, fd
= -1, listen_fd
= -1;
367 SRTContext
*s
= h
->priv_data
;
371 char hostname
[1024],proto
[1024],path
[1024];
373 int64_t open_timeout
= 0;
376 av_url_split(proto
, sizeof(proto
), NULL
, 0, hostname
, sizeof(hostname
),
377 &port
, path
, sizeof(path
), uri
);
378 if (strcmp(proto
, "srt"))
379 return AVERROR(EINVAL
);
380 if (port
<= 0 || port
>= 65536) {
381 av_log(h
, AV_LOG_ERROR
, "Port missing in uri\n");
382 return AVERROR(EINVAL
);
384 p
= strchr(uri
, '?');
386 if (av_find_info_tag(buf
, sizeof(buf
), "timeout", p
)) {
387 s
->rw_timeout
= strtol(buf
, NULL
, 10);
389 if (av_find_info_tag(buf
, sizeof(buf
), "listen_timeout", p
)) {
390 s
->listen_timeout
= strtol(buf
, NULL
, 10);
393 if (s
->rw_timeout
>= 0) {
394 open_timeout
= h
->rw_timeout
= s
->rw_timeout
;
396 hints
.ai_family
= AF_UNSPEC
;
397 hints
.ai_socktype
= SOCK_DGRAM
;
398 snprintf(portstr
, sizeof(portstr
), "%d", port
);
399 if (s
->mode
== SRT_MODE_LISTENER
)
400 hints
.ai_flags
|= AI_PASSIVE
;
401 ret
= getaddrinfo(hostname
[0] ? hostname
: NULL
, portstr
, &hints
, &ai
);
403 av_log(h
, AV_LOG_ERROR
,
404 "Failed to resolve hostname %s: %s\n",
405 hostname
, gai_strerror(ret
));
411 eid
= srt_epoll_create();
413 return libsrt_neterrno(h
);
418 fd
= srt_socket(cur_ai
->ai_family
, cur_ai
->ai_socktype
, 0);
420 ret
= libsrt_neterrno(h
);
424 if ((ret
= libsrt_set_options_pre(h
, fd
)) < 0) {
428 /* Set the socket's send or receive buffer sizes, if specified.
429 If unspecified or setting fails, system default is used. */
430 if (s
->recv_buffer_size
> 0) {
431 srt_setsockopt(fd
, SOL_SOCKET
, SRTO_UDP_RCVBUF
, &s
->recv_buffer_size
, sizeof (s
->recv_buffer_size
));
433 if (s
->send_buffer_size
> 0) {
434 srt_setsockopt(fd
, SOL_SOCKET
, SRTO_UDP_SNDBUF
, &s
->send_buffer_size
, sizeof (s
->send_buffer_size
));
436 if (libsrt_socket_nonblock(fd
, 1) < 0)
437 av_log(h
, AV_LOG_DEBUG
, "libsrt_socket_nonblock failed\n");
439 if (s
->mode
== SRT_MODE_LISTENER
) {
441 if ((ret
= libsrt_listen(s
->eid
, fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
, h
, s
->listen_timeout
)) < 0)
446 if (s
->mode
== SRT_MODE_RENDEZVOUS
) {
447 ret
= srt_bind(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
);
452 if ((ret
= libsrt_listen_connect(s
->eid
, fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
453 open_timeout
, h
, !!cur_ai
->ai_next
)) < 0) {
454 if (ret
== AVERROR_EXIT
)
460 if ((ret
= libsrt_set_options_post(h
, fd
)) < 0) {
464 if (flags
& AVIO_FLAG_WRITE
) {
466 int optlen
= sizeof(packet_size
);
467 ret
= libsrt_getsockopt(h
, fd
, SRTO_PAYLOADSIZE
, "SRTO_PAYLOADSIZE", &packet_size
, &optlen
);
471 h
->max_packet_size
= packet_size
;
476 s
->listen_fd
= listen_fd
;
482 if (cur_ai
->ai_next
) {
483 /* Retry with the next sockaddr */
484 cur_ai
= cur_ai
->ai_next
;
488 srt_close(listen_fd
);
496 srt_close(listen_fd
);
498 srt_epoll_release(s
->eid
);
502 static int libsrt_open(URLContext
*h
, const char *uri
, int flags
)
504 SRTContext
*s
= h
->priv_data
;
509 if (srt_startup() < 0) {
510 return AVERROR_UNKNOWN
;
513 /* SRT options (srt/srt.h) */
514 p
= strchr(uri
, '?');
516 if (av_find_info_tag(buf
, sizeof(buf
), "maxbw", p
)) {
517 s
->maxbw
= strtoll(buf
, NULL
, 0);
519 if (av_find_info_tag(buf
, sizeof(buf
), "pbkeylen", p
)) {
520 s
->pbkeylen
= strtol(buf
, NULL
, 10);
522 if (av_find_info_tag(buf
, sizeof(buf
), "passphrase", p
)) {
523 av_freep(&s
->passphrase
);
524 s
->passphrase
= av_strndup(buf
, strlen(buf
));
526 #if SRT_VERSION_VALUE >= 0x010302
527 if (av_find_info_tag(buf
, sizeof(buf
), "enforced_encryption", p
)) {
528 s
->enforced_encryption
= strtol(buf
, NULL
, 10);
530 if (av_find_info_tag(buf
, sizeof(buf
), "kmrefreshrate", p
)) {
531 s
->kmrefreshrate
= strtol(buf
, NULL
, 10);
533 if (av_find_info_tag(buf
, sizeof(buf
), "kmpreannounce", p
)) {
534 s
->kmpreannounce
= strtol(buf
, NULL
, 10);
537 if (av_find_info_tag(buf
, sizeof(buf
), "mss", p
)) {
538 s
->mss
= strtol(buf
, NULL
, 10);
540 if (av_find_info_tag(buf
, sizeof(buf
), "ffs", p
)) {
541 s
->ffs
= strtol(buf
, NULL
, 10);
543 if (av_find_info_tag(buf
, sizeof(buf
), "ipttl", p
)) {
544 s
->ipttl
= strtol(buf
, NULL
, 10);
546 if (av_find_info_tag(buf
, sizeof(buf
), "iptos", p
)) {
547 s
->iptos
= strtol(buf
, NULL
, 10);
549 if (av_find_info_tag(buf
, sizeof(buf
), "inputbw", p
)) {
550 s
->inputbw
= strtoll(buf
, NULL
, 10);
552 if (av_find_info_tag(buf
, sizeof(buf
), "oheadbw", p
)) {
553 s
->oheadbw
= strtoll(buf
, NULL
, 10);
555 if (av_find_info_tag(buf
, sizeof(buf
), "latency", p
)) {
556 s
->latency
= strtol(buf
, NULL
, 10);
558 if (av_find_info_tag(buf
, sizeof(buf
), "tsbpddelay", p
)) {
559 s
->latency
= strtol(buf
, NULL
, 10);
561 if (av_find_info_tag(buf
, sizeof(buf
), "rcvlatency", p
)) {
562 s
->rcvlatency
= strtol(buf
, NULL
, 10);
564 if (av_find_info_tag(buf
, sizeof(buf
), "peerlatency", p
)) {
565 s
->peerlatency
= strtol(buf
, NULL
, 10);
567 if (av_find_info_tag(buf
, sizeof(buf
), "tlpktdrop", p
)) {
568 s
->tlpktdrop
= strtol(buf
, NULL
, 10);
570 if (av_find_info_tag(buf
, sizeof(buf
), "nakreport", p
)) {
571 s
->nakreport
= strtol(buf
, NULL
, 10);
573 if (av_find_info_tag(buf
, sizeof(buf
), "connect_timeout", p
)) {
574 s
->connect_timeout
= strtol(buf
, NULL
, 10);
576 if (av_find_info_tag(buf
, sizeof(buf
), "payload_size", p
) ||
577 av_find_info_tag(buf
, sizeof(buf
), "pkt_size", p
)) {
578 s
->payload_size
= strtol(buf
, NULL
, 10);
580 if (av_find_info_tag(buf
, sizeof(buf
), "mode", p
)) {
581 if (!strcmp(buf
, "caller")) {
582 s
->mode
= SRT_MODE_CALLER
;
583 } else if (!strcmp(buf
, "listener")) {
584 s
->mode
= SRT_MODE_LISTENER
;
585 } else if (!strcmp(buf
, "rendezvous")) {
586 s
->mode
= SRT_MODE_RENDEZVOUS
;
588 ret
= AVERROR(EINVAL
);
592 if (av_find_info_tag(buf
, sizeof(buf
), "sndbuf", p
)) {
593 s
->sndbuf
= strtol(buf
, NULL
, 10);
595 if (av_find_info_tag(buf
, sizeof(buf
), "rcvbuf", p
)) {
596 s
->rcvbuf
= strtol(buf
, NULL
, 10);
598 if (av_find_info_tag(buf
, sizeof(buf
), "lossmaxttl", p
)) {
599 s
->lossmaxttl
= strtol(buf
, NULL
, 10);
601 if (av_find_info_tag(buf
, sizeof(buf
), "minversion", p
)) {
602 s
->minversion
= strtol(buf
, NULL
, 0);
604 if (av_find_info_tag(buf
, sizeof(buf
), "streamid", p
)) {
605 av_freep(&s
->streamid
);
606 s
->streamid
= av_strdup(buf
);
608 ret
= AVERROR(ENOMEM
);
612 if (av_find_info_tag(buf
, sizeof(buf
), "smoother", p
)) {
613 av_freep(&s
->smoother
);
614 s
->smoother
= av_strdup(buf
);
616 ret
= AVERROR(ENOMEM
);
620 if (av_find_info_tag(buf
, sizeof(buf
), "messageapi", p
)) {
621 s
->messageapi
= strtol(buf
, NULL
, 10);
623 if (av_find_info_tag(buf
, sizeof(buf
), "transtype", p
)) {
624 if (!strcmp(buf
, "live")) {
625 s
->transtype
= SRTT_LIVE
;
626 } else if (!strcmp(buf
, "file")) {
627 s
->transtype
= SRTT_FILE
;
629 ret
= AVERROR(EINVAL
);
633 if (av_find_info_tag(buf
, sizeof(buf
), "linger", p
)) {
634 s
->linger
= strtol(buf
, NULL
, 10);
637 ret
= libsrt_setup(h
, uri
, flags
);
643 av_freep(&s
->smoother
);
644 av_freep(&s
->streamid
);
649 static int libsrt_read(URLContext
*h
, uint8_t *buf
, int size
)
651 SRTContext
*s
= h
->priv_data
;
654 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
655 ret
= libsrt_network_wait_fd_timeout(h
, s
->eid
, s
->fd
, 0, h
->rw_timeout
, &h
->interrupt_callback
);
660 ret
= srt_recvmsg(s
->fd
, buf
, size
);
662 ret
= libsrt_neterrno(h
);
668 static int libsrt_write(URLContext
*h
, const uint8_t *buf
, int size
)
670 SRTContext
*s
= h
->priv_data
;
673 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
674 ret
= libsrt_network_wait_fd_timeout(h
, s
->eid
, s
->fd
, 1, h
->rw_timeout
, &h
->interrupt_callback
);
679 ret
= srt_sendmsg(s
->fd
, buf
, size
, -1, 0);
681 ret
= libsrt_neterrno(h
);
687 static int libsrt_close(URLContext
*h
)
689 SRTContext
*s
= h
->priv_data
;
693 if (s
->listen_fd
>= 0)
694 srt_close(s
->listen_fd
);
696 srt_epoll_release(s
->eid
);
703 static int libsrt_get_file_handle(URLContext
*h
)
705 SRTContext
*s
= h
->priv_data
;
709 static const AVClass libsrt_class
= {
710 .class_name
= "libsrt",
711 .item_name
= av_default_item_name
,
712 .option
= libsrt_options
,
713 .version
= LIBAVUTIL_VERSION_INT
,
716 const URLProtocol ff_libsrt_protocol
= {
718 .url_open
= libsrt_open
,
719 .url_read
= libsrt_read
,
720 .url_write
= libsrt_write
,
721 .url_close
= libsrt_close
,
722 .url_get_file_handle
= libsrt_get_file_handle
,
723 .priv_data_size
= sizeof(SRTContext
),
724 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
725 .priv_data_class
= &libsrt_class
,