[FFmpeg-devel] [PATCH 3/3] avformat/opensrt: add Haivision Open SRT protocol
Nablet Developer
sdk at nablet.com
Thu Nov 9 11:31:32 EET 2017
protocol requires libsrt (https://github.com/Haivision/srt) to be
installed
Signed-off-by: Nablet Developer <sdk at 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
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;
--
2.7.4
More information about the ffmpeg-devel
mailing list