diff mbox

[FFmpeg-devel,3/3] avformat/opensrt: add Haivision Open SRT protocol

Message ID 1510219892-8142-4-git-send-email-sdk@nablet.com
State Superseded
Headers show

Commit Message

Nablet Developer Nov. 9, 2017, 9:31 a.m. UTC
protocol requires libsrt (https://github.com/Haivision/srt) to be
installed

Signed-off-by: Nablet Developer <sdk@nablet.com>
---
 configure               |  10 ++
 libavformat/Makefile    |   1 +
 libavformat/opensrt.c   | 418 ++++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/protocols.c |   1 +
 4 files changed, 430 insertions(+)
 create mode 100644 libavformat/opensrt.c

Comments

Nicolas George Nov. 9, 2017, 8:35 p.m. UTC | #1
Le nonidi 19 brumaire, an CCXXVI, Nablet Developer a écrit :
> protocol requires libsrt (https://github.com/Haivision/srt) to be
> installed
> 
> Signed-off-by: Nablet Developer <sdk@nablet.com>
> ---
>  configure               |  10 ++
>  libavformat/Makefile    |   1 +
>  libavformat/opensrt.c   | 418 ++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/protocols.c |   1 +
>  4 files changed, 430 insertions(+)
>  create mode 100644 libavformat/opensrt.c

Can you explain the logic of the implementation? I have a hard time
understanding why you call the tcp protocol from here after forcing it
to call back here.

Regards,
Nablet Developer Nov. 10, 2017, 3:34 a.m. UTC | #2
On 10-Nov-17 03:35, Nicolas George wrote:
> Can you explain the logic of the implementation? I have a hard time
> understanding why you call the tcp protocol from here after forcing it
> to call back here.
>
> Regards,
>
>
the idea is to avoid code duplication as much as possible, and try to 
re-use existing, well-maintained and well-tested code.
this time I've chosen tcp.c rather udp.c for two reasons:
1. SRT socket API actually resembles tcp, as it uses connection and 
related methods (connect/listen/accept)
2. tcp.c code is much more clean and straightforward rather udp.c
the first thing which obviously differs between tcp and srt are socket 
api calls, but there is direct mapping for most of them, e.g.:
socket -> srt_socket
connect -> srt_connect
listen -> srt_listen
very few of srt socket calls are different, e.g. srt doesn't provide 
send/recv, but provides sendmsg/recvmsg, also it doesn't provide poll, 
but has epoll_wait.
with simple wrappers, it allows to use existing logic of tcp.c and 
network.c without modifications.
for calling back srt from tcp, that's the second difference - srt has 
lots of additional socket options,
and some of these socket options has to be set prior to connection, 
others after connection (called pre and post options in srt).
Haivision explicitly requested to add these options into ffmpeg component.
so, there are two calls back in tcp_open - to set options just before 
connection, and to set options right after connections.
if you have some advice on how it can be implemented better, I am open 
for suggestions and advises.
Nicolas George Nov. 16, 2017, 11:07 a.m. UTC | #3
Le decadi 20 brumaire, an CCXXVI, nablet developer a écrit :
> the idea is to avoid code duplication as much as possible, and try to re-use
> existing, well-maintained and well-tested code.
> this time I've chosen tcp.c rather udp.c for two reasons:
> 1. SRT socket API actually resembles tcp, as it uses connection and related
> methods (connect/listen/accept)
> 2. tcp.c code is much more clean and straightforward rather udp.c
> the first thing which obviously differs between tcp and srt are socket api
> calls, but there is direct mapping for most of them, e.g.:
> socket -> srt_socket
> connect -> srt_connect
> listen -> srt_listen
> very few of srt socket calls are different, e.g. srt doesn't provide
> send/recv, but provides sendmsg/recvmsg, also it doesn't provide poll, but
> has epoll_wait.
> with simple wrappers, it allows to use existing logic of tcp.c and network.c
> without modifications.
> for calling back srt from tcp, that's the second difference - srt has lots
> of additional socket options,
> and some of these socket options has to be set prior to connection, others
> after connection (called pre and post options in srt).
> Haivision explicitly requested to add these options into ffmpeg component.
> so, there are two calls back in tcp_open - to set options just before
> connection, and to set options right after connections.
> if you have some advice on how it can be implemented better, I am open for
> suggestions and advises.

Thanks for explaining. I think it is not the best decision.

The reason the socket API resembles TCP is because all the sockets API
resemble each other, since they are based on the old BSD socket API. And
the protocol handlers of libavformat too.

Therefore, I think all the trampoline code to allow TCP to call back
another protocol plus all the boilerplate code that you need to make
opensrc callable from TCP amounts to worse than implementing a protocol
directly.

Furthermore, TCP is the most important network protocol for now, while
opensrt is still rather obscure, so tying one with the other is not a
good idea.

Also, implementing a real protocol from scratch would possibly allow you
to make use of extra features of the opensrt API: maybe they have a
read-with-timeout function, for example, or something like that.

Regards,
Nablet Developer Nov. 20, 2017, 6:35 a.m. UTC | #4
> 
> Thanks for explaining. I think it is not the best decision.
> 
> The reason the socket API resembles TCP is because all the sockets API
> resemble each other, since they are based on the old BSD socket API. And
> the protocol handlers of libavformat too.
> 
> Therefore, I think all the trampoline code to allow TCP to call back
> another protocol plus all the boilerplate code that you need to make
> opensrc callable from TCP amounts to worse than implementing a protocol
> directly.
> 
> Furthermore, TCP is the most important network protocol for now, while
> opensrt is still rather obscure, so tying one with the other is not a
> good idea.
> 
> Also, implementing a real protocol from scratch would possibly allow you
> to make use of extra features of the opensrt API: maybe they have a
> read-with-timeout function, for example, or something like that.
> 
> 

thanks for the feedback.
regarding relying on TCP protocol code it's clear - I will implement protocol from scratch next time.
regarding re-using functions from network.c (like ff_network_wait_fd, ff_accept, etc)
is suggested approach acceptable? is it okay to define such socket_api structure and pass to
network.c calls?
Nablet Developer Nov. 22, 2017, 6:46 a.m. UTC | #5
> On 20 Nov 2017, at 13:35, nablet developer <sdk@nablet.com> wrote:
> 
> 
>> 
>> Thanks for explaining. I think it is not the best decision.
>> 
>> The reason the socket API resembles TCP is because all the sockets API
>> resemble each other, since they are based on the old BSD socket API. And
>> the protocol handlers of libavformat too.
>> 
>> Therefore, I think all the trampoline code to allow TCP to call back
>> another protocol plus all the boilerplate code that you need to make
>> opensrc callable from TCP amounts to worse than implementing a protocol
>> directly.
>> 
>> Furthermore, TCP is the most important network protocol for now, while
>> opensrt is still rather obscure, so tying one with the other is not a
>> good idea.
>> 
>> Also, implementing a real protocol from scratch would possibly allow you
>> to make use of extra features of the opensrt API: maybe they have a
>> read-with-timeout function, for example, or something like that.
>> 
>> 
> 
> thanks for the feedback.
> regarding relying on TCP protocol code it's clear - I will implement protocol from scratch next time.
> regarding re-using functions from network.c (like ff_network_wait_fd, ff_accept, etc)
> is suggested approach acceptable? is it okay to define such socket_api structure and pass to
> network.c calls?
> 

ping
Nicolas George Nov. 23, 2017, 8:07 p.m. UTC | #6
nablet developer (2017-11-20):
> regarding re-using functions from network.c (like ff_network_wait_fd, ff_accept, etc)
> is suggested approach acceptable? is it okay to define such socket_api structure and pass to
> network.c calls?

I do not think so. These functions are very specific to the quirks of
the BSD socket API, and not very well designed at that. I would say to
only use them if you actually have a file descriptor. Hopefully, the
library should provide better base APIs, like operations with a timeout.

Regards,
diff mbox

Patch

diff --git a/configure b/configure
index f396abd..b44df0e 100755
--- a/configure
+++ b/configure
@@ -293,6 +293,7 @@  External library support:
   --enable-opengl          enable OpenGL rendering [no]
   --enable-openssl         enable openssl, needed for https support
                            if gnutls is not used [no]
+  --enable-opensrt         enable Haivision Open SRT protoco [no]
   --disable-sndio          disable sndio support [autodetect]
   --disable-schannel       disable SChannel SSP, needed for TLS support on
                            Windows if openssl and gnutls are not used [autodetect]
@@ -1638,6 +1639,7 @@  EXTERNAL_LIBRARY_LIST="
     openal
     opencl
     opengl
+    opensrt
 "
 
 HWACCEL_AUTODETECT_LIBRARY_LIST="
@@ -3139,6 +3141,8 @@  libsmbclient_protocol_deps="libsmbclient gplv3"
 libssh_protocol_deps="libssh"
 mmsh_protocol_select="http_protocol"
 mmst_protocol_select="network"
+opensrt_protocol_select="network"
+opensrt_protocol_deps="opensrt"
 rtmp_protocol_conflict="librtmp_protocol"
 rtmp_protocol_select="tcp_protocol"
 rtmp_protocol_suggest="zlib"
@@ -6102,6 +6106,8 @@  enabled omx               && require_header OMX_Core.h
 enabled omx_rpi           && { check_header OMX_Core.h ||
                                { ! enabled cross_compile && add_cflags -isystem/opt/vc/include/IL && check_header OMX_Core.h ; } ||
                                die "ERROR: OpenMAX IL headers not found"; } && enable omx
+#enabled opensrt           && check_lib srt srt/srt.h srt_socket -lsrt
+enabled opensrt           && require_pkg_config libsrt "srt >= 1.2.0" srt/srt.h srt_socket
 enabled openssl           && { use_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl ||
                                use_pkg_config openssl openssl openssl/ssl.h SSL_library_init ||
                                check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto ||
@@ -6156,6 +6162,10 @@  if enabled decklink; then
     esac
 fi
 
+if enabled opensrt; then
+    opensrt_protocol_extralibs="$opensrt_protocol_extralibs -lsrt"
+fi
+
 enabled securetransport &&
     check_func SecIdentityCreate "-Wl,-framework,CoreFoundation -Wl,-framework,Security" &&
     check_lib securetransport "Security/SecureTransport.h Security/Security.h" "SSLCreateContext SecItemImport" "-Wl,-framework,CoreFoundation -Wl,-framework,Security" ||
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 146a465..5116d31 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -596,6 +596,7 @@  TLS-OBJS-$(CONFIG_SCHANNEL)              += tls_schannel.o
 OBJS-$(CONFIG_TLS_PROTOCOL)              += tls.o $(TLS-OBJS-yes)
 OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o
 OBJS-$(CONFIG_UDPLITE_PROTOCOL)          += udp.o
+OBJS-$(CONFIG_OPENSRT_PROTOCOL)          += opensrt.o
 OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o
 
 # libavdevice dependencies
diff --git a/libavformat/opensrt.c b/libavformat/opensrt.c
new file mode 100644
index 0000000..bc58368
--- /dev/null
+++ b/libavformat/opensrt.c
@@ -0,0 +1,418 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Haivision Open SRT (Secure Reliable Transport) protocol
+ */
+
+#include "avformat.h"
+#include "libavutil/avassert.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/time.h"
+
+#include "internal.h"
+#include "network.h"
+#include "os_support.h"
+#include "url.h"
+#if HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#include "tcp.h"
+
+#if CONFIG_OPENSRT_PROTOCOL
+#include <srt/srt.h>
+#endif
+
+enum SRTMode {
+    SRT_MODE_CALLER = 0,
+    SRT_MODE_LISTENER = 1,
+    SRT_MODE_RENDEZVOUS = 2
+};
+
+typedef struct SRTContext {
+    struct TCPContext tcp_context;
+    /* SRT socket options (srt/srt.h) */
+    int64_t maxbw;
+    int pbkeylen;
+    char * passphrase;
+    int mss;
+    int fc;
+    int ipttl;
+    int iptos;
+    int64_t inputbw;
+    int64_t oheadbw;
+    int tsbpddelay;
+    int tlpktdrop;
+    int nakreport;
+    int conntimeo;
+    enum SRTMode mode;
+} SRTContext;
+
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+#define OFFSETSRT(x) offsetof(SRTContext, x)
+static const AVOption opensrt_options[] = {
+    TCP_COMMON_OPTS
+    /* SRT socket options (srt/srt.h) */
+    { "maxbw",          "maximum bandwidth (bytes per second) that the connection can use",            OFFSETSRT(maxbw),      AV_OPT_TYPE_INT64,  { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
+    { "pbkeylen",       "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)",                    OFFSETSRT(pbkeylen),   AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, 32,        .flags = D|E },
+    { "passphrase",     "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto",                    OFFSETSRT(passphrase), AV_OPT_TYPE_STRING, { .str = NULL },              .flags = D|E },
+    { "mss",            "the Maximum Transfer Unit",                                                   OFFSETSRT(mss),        AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "fc",             "Flight flag size (window size)",                                              OFFSETSRT(fc),         AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "ipttl",          "IP Time To Live",                                                             OFFSETSRT(ipttl),      AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "iptos",          "IP Type of Service",                                                          OFFSETSRT(iptos),      AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "inputbw",        "Estimated input stream rate",                                                 OFFSETSRT(inputbw),    AV_OPT_TYPE_INT64,  { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
+    { "oheadbw",        "MaxBW ceiling based on % over input stream rate",                             OFFSETSRT(oheadbw),    AV_OPT_TYPE_INT64,  { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
+    { "tsbpddelay",     "TsbPd receiver delay (mSec) to absorb burst of missed packet retransmission", OFFSETSRT(tsbpddelay), AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "tlpktdrop",      "Enable receiver pkt drop",                                                    OFFSETSRT(tlpktdrop),  AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, 1,         .flags = D|E },
+    { "nakreport",      "Enable receiver to send periodic NAK reports",                                OFFSETSRT(nakreport),  AV_OPT_TYPE_INT,    { .i64 = -1 }, -1, 1,         .flags = D|E },
+    { "conntimeo",      "Connect timeout in msec. Ccaller default: 3000, rendezvous (x 10)",           OFFSETSRT(conntimeo),  AV_OPT_TYPE_INT64,  { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
+    { "mode",           "Connection mode (caller, listener, rendezvous)",                              OFFSETSRT(mode),       AV_OPT_TYPE_INT,    { .i64 = SRT_MODE_CALLER }, SRT_MODE_CALLER, SRT_MODE_RENDEZVOUS, .flags = D|E },
+    { "caller",         NULL, 0, AV_OPT_TYPE_CONST,  { .i64 = SRT_MODE_CALLER },     INT_MIN, INT_MAX, .flags = D|E },
+    { "listener",       NULL, 0, AV_OPT_TYPE_CONST,  { .i64 = SRT_MODE_LISTENER },   INT_MIN, INT_MAX, .flags = D|E },
+    { "rendezvous",     NULL, 0, AV_OPT_TYPE_CONST,  { .i64 = SRT_MODE_RENDEZVOUS }, INT_MIN, INT_MAX, .flags = D|E },
+    { NULL }
+};
+
+static const AVClass opensrt_class = {
+    .class_name = "opensrt",
+    .item_name  = av_default_item_name,
+    .option     = opensrt_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static int opensrt_neterrno(void)
+{
+    int err = srt_getlasterror(NULL);
+    if (err == SRT_EASYNCRCV)
+        return AVERROR(EAGAIN);
+    return AVERROR(EINVAL);
+}
+
+static int opensrt_socket_nonblock(int socket, int enable)
+{
+    int ret = srt_setsockopt(socket, 0, SRTO_SNDSYN, &enable, sizeof(enable));
+    if (ret < 0)
+        return ret;
+    ret = srt_setsockopt(socket, 0, SRTO_RCVSYN, &enable, sizeof(enable));
+    return ret;
+}
+
+static int opensrt_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+    int eid, ret, len = 1;
+    int modes = fds[0].events;
+    SRTSOCKET ready[1];
+    eid = srt_epoll_create();
+    if (eid < 0)
+        return eid;
+    ret = srt_epoll_add_usock(eid, fds[0].fd, &modes);
+    if (ret < 0) {
+        srt_epoll_release(eid);
+        return ret;
+    }
+    if (fds[0].events & POLLOUT) {
+        ret = srt_epoll_wait(eid, 0, 0, ready, &len, timeout, 0, 0, 0, 0);
+    } else {
+        ret = srt_epoll_wait(eid, ready, &len, 0, 0, timeout, 0, 0, 0, 0);
+    }
+    if (ret > 0) {
+        fds[0].revents = fds[0].events;
+    } else if (ret == 0) {
+        fds[0].revents = POLLERR;
+    } else {
+        if (srt_getlasterror(NULL) == SRT_ETIMEOUT)
+            ret = 0;
+    }
+    srt_epoll_release(eid);
+    return ret;
+}
+
+static ssize_t opensrt_send(int sockfd, const void *buf, size_t len, av_unused int flags)
+{
+    return srt_sendmsg(sockfd, buf, len, -1, 0);
+}
+
+static ssize_t opensrt_recv(int sockfd, void *buf, size_t len, av_unused int flags)
+{
+    return srt_recvmsg(sockfd, buf, len);
+}
+
+static int opensrt_socket(int domain, int type, int protocol)
+{
+    type = SOCK_DGRAM;
+    protocol = 0;
+    return srt_socket(domain, type, protocol);
+}
+
+static int opensrt_close_socket(int fd)
+{
+    return srt_close(fd);
+}
+
+static int opensrt_listen(int sockfd, int backlog)
+{
+    return srt_listen(sockfd, backlog);
+}
+
+static int opensrt_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+    return srt_bind(sockfd, addr, addrlen);
+}
+
+static int opensrt_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+    return srt_connect(sockfd, addr, addrlen);
+}
+
+static int opensrt_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+    return srt_accept(sockfd, addr, addrlen);
+}
+
+static int opensrt_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
+{
+    if (level == SOL_SOCKET) {
+        if (optname == SO_RCVBUF) {
+            optname = SRTO_RCVBUF;
+        } else if (optname == SO_SNDBUF) {
+            optname = SRTO_SNDBUF;
+        } else if (optname == SO_REUSEADDR) {
+            optname = SRTO_REUSEADDR;
+        } else if (optname == SO_ERROR && optlen && optval && *optlen == sizeof(int)) {
+            *(int*)optval = srt_getlasterror(NULL);
+            srt_clearlasterror();
+            return 0;
+        } else
+            return -1;
+    }
+    return srt_getsockopt(sockfd, level, optname, optval, optlen);
+}
+
+static int opensrt_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
+{
+    if (level == SOL_SOCKET) {
+        if (optname == SO_RCVBUF) {
+            optname = SRTO_RCVBUF;
+        } else if (optname == SO_SNDBUF) {
+            optname = SRTO_SNDBUF;
+        } else if (optname == SO_REUSEADDR) {
+            optname = SRTO_REUSEADDR;
+        } else
+            return -1;
+    }
+    return srt_setsockopt(sockfd, level, optname, optval, optlen);
+}
+
+static int opensrt_shutdown(av_unused int sockfd, av_unused int how)
+{
+    // not implemented in SRT yet
+    return 0;
+}
+
+const struct socket_api srt_socket_api = {
+    .poll = opensrt_poll,
+    .bind = opensrt_bind,
+    .socket = opensrt_socket,
+    .listen = opensrt_listen,
+    .connect = opensrt_connect,
+    .setsockopt = opensrt_setsockopt,
+    .getsockopt = opensrt_getsockopt,
+    .accept = opensrt_accept,
+    .close = opensrt_close_socket,
+    .socket_nonblock = opensrt_socket_nonblock,
+    .neterrno = opensrt_neterrno,
+    .send = opensrt_send,
+    .recv = opensrt_recv,
+    .shutdown = opensrt_shutdown
+};
+
+/* - The "POST" options can be altered any time on a connected socket.
+     They MAY have also some meaning when set prior to connecting; such
+     option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
+     Because of that this option is treated special way in this app. */
+static int opensrt_set_options_post(URLContext *h, int fd)
+{
+    SRTContext *s = h->priv_data;
+
+    if (s->inputbw >= 0 && srt_setsockopt(fd, 0, SRTO_INPUTBW, &s->inputbw, sizeof(s->inputbw)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_INPUTBW on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->oheadbw >= 0 && srt_setsockopt(fd, 0, SRTO_OHEADBW, &s->oheadbw, sizeof(s->oheadbw)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_OHEADBW on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    return 0;
+}
+
+/* - The "PRE" options must be set prior to connecting and can't be altered
+     on a connected socket, however if set on a listening socket, they are
+     derived by accept-ed socket. */
+static int opensrt_set_options_pre(URLContext *h, int fd)
+{
+    SRTContext *s = h->priv_data;
+    int yes = 1;
+
+    if (s->mode == SRT_MODE_RENDEZVOUS && srt_setsockopt(fd, 0, SRTO_RENDEZVOUS, &yes, sizeof(yes)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_RENDEZVOUS on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->maxbw >= 0 && srt_setsockopt(fd, 0, SRTO_MAXBW, &s->maxbw, sizeof(s->maxbw)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_MAXBW on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->pbkeylen >= 0 && srt_setsockopt(fd, 0, SRTO_PBKEYLEN, &s->pbkeylen, sizeof(s->pbkeylen)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_PBKEYLEN on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->passphrase[0] && srt_setsockopt(fd, 0, SRTO_PASSPHRASE, &s->passphrase, sizeof(s->passphrase)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_PASSPHRASE on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->mss >= 0 && srt_setsockopt(fd, 0, SRTO_MSS, &s->mss, sizeof(s->mss)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_MSS on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->fc >= 0 && srt_setsockopt(fd, 0, SRTO_FC, &s->fc, sizeof(s->fc)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_FC on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->ipttl >= 0 && srt_setsockopt(fd, 0, SRTO_IPTTL, &s->ipttl, sizeof(s->ipttl)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_IPTTL on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->iptos >= 0 && srt_setsockopt(fd, 0, SRTO_IPTOS, &s->iptos, sizeof(s->iptos)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_IPTOS on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->tsbpddelay >= 0 && srt_setsockopt(fd, 0, SRTO_TSBPDDELAY, &s->tsbpddelay, sizeof(s->tsbpddelay)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_TSBPDDELAY on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->tlpktdrop >= 0 && srt_setsockopt(fd, 0, SRTO_TLPKTDROP, &s->tlpktdrop, sizeof(s->tlpktdrop)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_TLPKTDROP on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->nakreport >= 0 && srt_setsockopt(fd, 0, SRTO_NAKREPORT, &s->nakreport, sizeof(s->nakreport)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_NAKREPORT on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    if (s->conntimeo >= 0 && srt_setsockopt(fd, 0, SRTO_CONNTIMEO, &s->conntimeo, sizeof(s->conntimeo)) < 0) {
+        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_CONNTIMEO on socket: %s", srt_getlasterror_str());
+        return AVERROR(EIO);
+    }
+    return 0;
+}
+
+static int opensrt_open(URLContext *h, const char *uri, int flags)
+{
+    SRTContext *s = h->priv_data;
+    const char * p;
+    char buf[256];
+
+    if (srt_startup() < 0) {
+        return AVERROR(EIO);
+    }
+
+    s->tcp_context.api = &srt_socket_api;
+    s->tcp_context.set_options_pre = opensrt_set_options_pre;
+    s->tcp_context.set_options_post = opensrt_set_options_post;
+    s->tcp_context.proto = "srt";
+    /* SRT options (srt/srt.h) */
+    p = strchr(uri, '?');
+    if (p)
+    {
+        if (av_find_info_tag(buf, sizeof(buf), "maxbw", p)) {
+            s->maxbw = strtoll(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "pbkeylen", p)) {
+            s->pbkeylen = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) {
+            s->passphrase = av_strndup(buf, strlen(buf));
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "mss", p)) {
+            s->mss = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "fc", p)) {
+            s->fc = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "ipttl", p)) {
+            s->ipttl = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "iptos", p)) {
+            s->iptos = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "inputbw", p)) {
+            s->inputbw = strtoll(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "oheadbw", p)) {
+            s->oheadbw = strtoll(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "tsbpddelay", p)) {
+            s->tsbpddelay = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "tlpktdrop", p)) {
+            s->tlpktdrop = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "nakreport", p)) {
+            s->nakreport = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "conntimeo", p)) {
+            s->conntimeo = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "mode", p)) {
+            if (!strcmp(buf, "caller")) {
+                s->mode = SRT_MODE_CALLER;
+            } else if (!strcmp(buf, "listener")) {
+                s->mode = SRT_MODE_LISTENER;
+            } else if (!strcmp(buf, "rendezvous")) {
+                s->mode = SRT_MODE_RENDEZVOUS;
+            }
+        }
+    }
+    return ff_tcp_open(h, uri, flags);
+}
+
+static int opensrt_close(URLContext *h)
+{
+    int ret = ff_tcp_close(h);
+
+    srt_cleanup();
+
+    return ret;
+}
+
+const URLProtocol ff_opensrt_protocol = {
+    .name                = "srt",
+    .url_open            = opensrt_open,
+    .url_accept          = ff_tcp_accept,
+    .url_read            = ff_tcp_read,
+    .url_write           = ff_tcp_write,
+    .url_close           = opensrt_close,
+    .url_get_file_handle = ff_tcp_get_file_handle,
+    .url_get_short_seek  = ff_tcp_get_window_size,
+    .url_shutdown        = ff_tcp_shutdown,
+    .priv_data_size      = sizeof(SRTContext),
+    .flags               = URL_PROTOCOL_FLAG_NETWORK,
+    .priv_data_class     = &opensrt_class,
+};
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index 669d74d..823349a 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -59,6 +59,7 @@  extern const URLProtocol ff_tcp_protocol;
 extern const URLProtocol ff_tls_protocol;
 extern const URLProtocol ff_udp_protocol;
 extern const URLProtocol ff_udplite_protocol;
+extern const URLProtocol ff_opensrt_protocol;
 extern const URLProtocol ff_unix_protocol;
 extern const URLProtocol ff_librtmp_protocol;
 extern const URLProtocol ff_librtmpe_protocol;