diff mbox

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

Message ID CACcSy=G=_FrgYXR_d810BrLG42EmE9NLqa=J7kr8kQAD7Bwh-A@mail.gmail.com
State New
Headers show

Commit Message

pkv.stream Nov. 9, 2017, 12:09 p.m. UTC
Le 9 nov. 2017 10:32 AM, "Nablet Developer" <sdk@nablet.com> 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

--
2.7.4
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;