diff mbox

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

Message ID 1517346663-4462-1-git-send-email-sdk@nablet.com
State New
Headers show

Commit Message

Nablet Developer Jan. 30, 2018, 9:11 p.m. UTC
protocol requires libsrt (https://github.com/Haivision/srt) to be
installed

Signed-off-by: Nablet Developer <sdk@nablet.com>
---
 MAINTAINERS             |   1 +
 configure               |   9 +
 doc/protocols.texi      | 116 +++++++++
 libavformat/Makefile    |   1 +
 libavformat/opensrt.c   | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/protocols.c |   1 +
 6 files changed, 749 insertions(+)
 create mode 100644 libavformat/opensrt.c

Comments

Nablet Developer Feb. 8, 2018, 8:20 a.m. UTC | #1
On 1/31/2018 4:11 AM, Nablet Developer wrote:
> protocol requires libsrt (https://github.com/Haivision/srt) to be
> installed
>
> Signed-off-by: Nablet Developer <sdk@nablet.com>
> ---
ping
Michael Niedermayer Feb. 9, 2018, 2:08 a.m. UTC | #2
On Tue, Jan 30, 2018 at 04:11:03PM -0500, Nablet Developer wrote:
> protocol requires libsrt (https://github.com/Haivision/srt) to be
> installed
> 
> Signed-off-by: Nablet Developer <sdk@nablet.com>
> ---
>  MAINTAINERS             |   1 +
>  configure               |   9 +
>  doc/protocols.texi      | 116 +++++++++
>  libavformat/Makefile    |   1 +
>  libavformat/opensrt.c   | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/protocols.c |   1 +
>  6 files changed, 749 insertions(+)
>  create mode 100644 libavformat/opensrt.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ba7a728..0317f24 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -498,6 +498,7 @@ Protocols:
>    http.c                                Ronald S. Bultje
>    libssh.c                              Lukasz Marek
>    mms*.c                                Ronald S. Bultje
> +  opensrt.c                             Nablet Developer
>    udp.c                                 Luca Abeni
>    icecast.c                             Marvin Scholz

Is "Nablet Developer" identifying a single person ?
if not this is not ok as the MAINTAINERS files gives one also the right to
have git write access and that would not work with a unspecified group


[...]
wm4 Feb. 9, 2018, 1:28 p.m. UTC | #3
On Fri, 9 Feb 2018 03:08:26 +0100
Michael Niedermayer <michael@niedermayer.cc> wrote:

> On Tue, Jan 30, 2018 at 04:11:03PM -0500, Nablet Developer wrote:
> > protocol requires libsrt (https://github.com/Haivision/srt) to be
> > installed
> > 
> > Signed-off-by: Nablet Developer <sdk@nablet.com>
> > ---
> >  MAINTAINERS             |   1 +
> >  configure               |   9 +
> >  doc/protocols.texi      | 116 +++++++++
> >  libavformat/Makefile    |   1 +
> >  libavformat/opensrt.c   | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
> >  libavformat/protocols.c |   1 +
> >  6 files changed, 749 insertions(+)
> >  create mode 100644 libavformat/opensrt.c
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index ba7a728..0317f24 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -498,6 +498,7 @@ Protocols:
> >    http.c                                Ronald S. Bultje
> >    libssh.c                              Lukasz Marek
> >    mms*.c                                Ronald S. Bultje
> > +  opensrt.c                             Nablet Developer
> >    udp.c                                 Luca Abeni
> >    icecast.c                             Marvin Scholz  
> 
> Is "Nablet Developer" identifying a single person ?
> if not this is not ok as the MAINTAINERS files gives one also the right to
> have git write access and that would not work with a unspecified group

I'd assume this is under the copyright of that nablet company. I'd
advise against just giving some drive-by contributor git push
access (who is probably multiple persons under a single account too). I
have no idea why you'd even consider that.

Also, the irony of the situation is that the whole diff context in this
patch consists of outdated entries. Ronald basically doesn't care
about HTTP or MMS anymore, Lukasz didn't post here for 2 years, Luca
didn't post here for at least 5 years, and refused my mpv relicensing
request because he has utterly negative memories of ffmpeg & co, and
Marvin didn't post here for about 4 years.
Michael Niedermayer Feb. 9, 2018, 2:10 p.m. UTC | #4
On Fri, Feb 09, 2018 at 02:28:41PM +0100, wm4 wrote:
> On Fri, 9 Feb 2018 03:08:26 +0100
> Michael Niedermayer <michael@niedermayer.cc> wrote:
> 
> > On Tue, Jan 30, 2018 at 04:11:03PM -0500, Nablet Developer wrote:
> > > protocol requires libsrt (https://github.com/Haivision/srt) to be
> > > installed
> > > 
> > > Signed-off-by: Nablet Developer <sdk@nablet.com>
> > > ---
> > >  MAINTAINERS             |   1 +
> > >  configure               |   9 +
> > >  doc/protocols.texi      | 116 +++++++++
> > >  libavformat/Makefile    |   1 +
> > >  libavformat/opensrt.c   | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
> > >  libavformat/protocols.c |   1 +
> > >  6 files changed, 749 insertions(+)
> > >  create mode 100644 libavformat/opensrt.c
> > > 
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index ba7a728..0317f24 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -498,6 +498,7 @@ Protocols:
> > >    http.c                                Ronald S. Bultje
> > >    libssh.c                              Lukasz Marek
> > >    mms*.c                                Ronald S. Bultje
> > > +  opensrt.c                             Nablet Developer
> > >    udp.c                                 Luca Abeni
> > >    icecast.c                             Marvin Scholz  
> > 
> > Is "Nablet Developer" identifying a single person ?
> > if not this is not ok as the MAINTAINERS files gives one also the right to
> > have git write access and that would not work with a unspecified group
> 
> I'd assume this is under the copyright of that nablet company. I'd
> advise against just giving some drive-by contributor git push
> access (who is probably multiple persons under a single account too). I
> have no idea why you'd even consider that.

If you or any other developer is against someone receiving git write access
you only need to object to the patch adding him to the MAINTAINERS file.

Thats one of the reasons why there is a MAINTAINERs file.

It would be ideal IMO if for each part of teh codebase at least one person 
with write access cares/"Maintains" it. Is there someone who already has
write access who wants to take care of opensrt.c ?
(it seemed there is little interrest from teh lack of replies in the thread so
far)

Also i belive there already is a developer from nablet who is in the
MAINTAINERs file and has write access.

thanks

[...]
Nicolas George Feb. 11, 2018, 7:04 p.m. UTC | #5
Hi.

I had a look at the whole code. There are a few remarks below.

Sorry for the delay, a lot of things on my place these days.

Nablet Developer (2018-01-30):
> protocol requires libsrt (https://github.com/Haivision/srt) to be
> installed
> 
> Signed-off-by: Nablet Developer <sdk@nablet.com>
> ---
>  MAINTAINERS             |   1 +
>  configure               |   9 +
>  doc/protocols.texi      | 116 +++++++++
>  libavformat/Makefile    |   1 +
>  libavformat/opensrt.c   | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/protocols.c |   1 +
>  6 files changed, 749 insertions(+)
>  create mode 100644 libavformat/opensrt.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ba7a728..0317f24 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -498,6 +498,7 @@ Protocols:
>    http.c                                Ronald S. Bultje
>    libssh.c                              Lukasz Marek
>    mms*.c                                Ronald S. Bultje
> +  opensrt.c                             Nablet Developer
>    udp.c                                 Luca Abeni
>    icecast.c                             Marvin Scholz
>  
> diff --git a/configure b/configure
> index fcfa7aa..57705ee 100755
> --- a/configure
> +++ b/configure
> @@ -294,6 +294,7 @@ External library support:
>    --enable-opengl          enable OpenGL rendering [no]
>    --enable-openssl         enable openssl, needed for https support
>                             if gnutls or libtls is not used [no]
> +  --enable-opensrt         enable Haivision Open SRT protocol [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]
> @@ -1641,6 +1642,7 @@ EXTERNAL_LIBRARY_LIST="
>      mediacodec
>      openal
>      opengl
> +    opensrt
>  "
>  
>  HWACCEL_AUTODETECT_LIBRARY_LIST="
> @@ -3148,6 +3150,8 @@ libssh_protocol_deps="libssh"
>  libtls_conflict="openssl gnutls"
>  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"
> @@ -5986,6 +5990,7 @@ 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           && require_pkg_config libsrt "srt >= 1.2.0" srt/srt.h srt_socket
>  enabled openssl           && { check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl ||
>                                 check_pkg_config openssl openssl openssl/ssl.h SSL_library_init ||
>                                 check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto ||
> @@ -6036,6 +6041,10 @@ if enabled decklink; then
>      esac
>  fi
>  

> +if enabled opensrt; then
> +    opensrt_protocol_extralibs="$opensrt_protocol_extralibs -lsrt"
> +fi

This looks suspicious: pkg-config should have added -lsrt automatically.

> +
>  enabled securetransport &&
>      check_func SecIdentityCreate "-Wl,-framework,CoreFoundation -Wl,-framework,Security" &&
>      check_lib securetransport "Security/SecureTransport.h Security/Security.h" "SSLCreateContext" "-Wl,-framework,CoreFoundation -Wl,-framework,Security" ||
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index 98deb73..2e5e630 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -755,6 +755,122 @@ Set the workgroup used for making connections. By default workgroup is not speci
>  
>  For more information see: @url{http://www.samba.org/}.
>  
> +@section srt
> +
> +Haivision Secure Reliable Transport Protocol via libsrt.
> +
> +The required syntax for a SRT url is:
> +@example
> +srt://@var{hostname}:@var{port}[?@var{options}]
> +@end example
> +
> +@var{options} contains a list of &-separated options of the form
> +@var{key}=@var{val}.
> +
> +This protocol accepts the following options.
> +
> +@table @option

> +@item conntimeo

Please do not truncate the name.

> +Connection timeout. SRT cannot connect for RTT > 1500 msec
> +(2 handshake exchanges) with the default connect timeout of 3 seconds. This option
> +applies to the caller and rendezvous connection modes. The connect timeout is 10 times
> +the value set for the rendezvous mode (which can be used as a workaround for this
> +connection problem with earlier versions).

Nit: maybe wrap the lines shorter, longer lines are more tiring to read.

> +
> +@item fc=@var{bytes}
> +Flight Flag Size (Window Size), in bytes. FC is actually an internal parameter and
> +you should set it to not less than @option{recv_buffer_size} and @option{mss}.
> +The default value is relatively large, therefore unless you set a very large
> +receiver buffer, you do not need to change this option. Default value is 25600.
> +
> +@item inputbw=@var{bytes/seconds}
> +Sender nominal input rate, in bytes per seconds. Used along with @option{oheadbw},
> +when @option{maxbw} is set to relative (0), to calculate maximum sending rate when
> +recovery packets are sent along with main media stream:
> +@option{inputbw} * (100 + @option{oheadbw}) / 100
> +if @option{inputbw} is not set while @option{maxbw} is set to relative (0), the actual
> +ctual input rate is evaluated inside the library. Default value is 0.
> +
> +@item iptos=@var{tos}
> +IP Type of Service. Applies to sender only. Default value is 0xB8.
> +
> +@item ipttl=@var{ttl}
> +IP Time To Live. Applies to sender only. Default value is 64.
> +
> +@item listen_timeout
> +Set socket listen timeout.
> +
> +@item maxbw=@var{bytes/seconds}
> +Maximum sending bandwidth, in bytes per seconds.
> +-1 infinite (CSRTCC limit is 30mbps)
> +0 relative to input rate (see @option{inputbw})
> +>0 absolute limit value
> +Default value is 0 (relative)
> +
> +@item mode=@var{caller|listener|rendezvous}
> +Connection mode.
> +caller opens client connection.
> +listener starts server to listen for incoming connections.
> +rendezvous use Rendez-Vous connection mode.
> +Default valus is caller.
> +
> +@item mss=@var{bytes}
> +Maximum Segment Size, in bytes. Used for buffer allocation and rate calculation using
> +packet counter assuming fully filled packets. The smallest MSS between the peers is
> +used. This is 1500 by default in the overall internet. This is the maximum size of the
> +UDP packet and can be only decreased, unless you have some unusual dedicated network
> +settings. Default value is 1500.
> +
> +@item nakreport=@var{1|0}
> +If set to 1, Receiver will send `UMSG_LOSSREPORT` messages periodically until the
> +lost packet is retransmitted or intentionally dropped. Default value is 1.
> +
> +@item oheadbw=@var{percents}
> +Recovery bandwidth overhead above input rate, in percents. See @option{inputbw}.
> +Default value is 25%.
> +
> +@item passphrase=@var{string}
> +HaiCrypt Encryption/Decryption Passphrase string, length from 10 to 79 characters.
> +The passphrase is the shared secret between the sender and the receiver.
> +It is used to generate the Key Encrypting Key using PBKDF2 (Password-Based
> +Key Deriviation Function). It is used only if @option{pbkeylen} is non-zero.
> +t is used on the receiver only if the received data is encrypted.
> +The configured passphrase cannot be get back (write-only).
> +
> +@item pbkeylen=@var{bytes}
> +Sender encryption key length, in bytes. Only can be set to 0, 16, 24 and 32.
> +Enable sender encryption if not 0. Not required on receiver (set to 0),
> +key size obtained from sender in HaiCrypt handshake. Default value is 0.
> +
> +@item recv_buffer_size=@var{bytes}
> +Set receive buffer size, expressed bytes.
> +
> +@item send_buffer_size=@var{bytes}
> +Set send buffer size, expressed bytes.
> +
> +@item timeout
> +Set raise error timeout.
> +
> +This option is only relevant in read mode: if no data arrived in more
> +than this time interval, raise error.
> +
> +@item tlpktdrop=@var{1|0}
> +Too-late Packet Drop. When enabled on receiver, it skips missing packets that
> +have not been delivered in time and deliver the following packets to the application
> +when their time-to-play has come. It also send a fake ACK to sender. When enabled on
> +sender and enabled on the receiving peer, sender drops the older packets that have no
> +chance to be delivered in time. It was automatically enabled in sender if receiver
> +supports it.
> +
> +@item tsbpddelay
> +Timestamp-based Packet Delivery Delay.
> +Used to absorb burst of missed packet retransmission.
> +
> +@end table
> +
> +For more information see: @url{https://github.com/Haivision/srt}.
> +
> +
>  @section libssh
>  
>  Secure File Transfer Protocol via libssh
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index de0de92..bd92071 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -598,6 +598,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..0b16391
> --- /dev/null
> +++ b/libavformat/opensrt.c
> @@ -0,0 +1,621 @@
> +/*
> + * 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
> +
> +#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 {
> +    int fd;

> +    int rw_timeout;

All AV_OPT_TYPE_DURATION fields need to be int64_t.

> +    int listen_timeout;
> +    int recv_buffer_size;
> +    int send_buffer_size;
> +
> +    int64_t maxbw;
> +    int pbkeylen;
> +    char * passphrase;
> +    int mss;
> +    int fc;
> +    int ipttl;
> +    int iptos;
> +    int64_t inputbw;
> +    int 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 OFFSET(x) offsetof(SRTContext, x)
> +static const AVOption opensrt_options[] = {
> +    { "timeout",        "set timeout of socket I/O operations",                                 OFFSET(rw_timeout),       AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
> +    { "listen_timeout", "Connection awaiting timeout",                                          OFFSET(listen_timeout),   AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
> +    { "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 },
> +    { "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 },
> +    { "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 },
> +    { "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 },
> +    { "passphrase",     "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto",             OFFSET(passphrase),       AV_OPT_TYPE_STRING,   { .str = NULL },              .flags = D|E },
> +    { "mss",            "the Maximum Transfer Unit",                                            OFFSET(mss),              AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1500,      .flags = D|E },
> +    { "fc",             "Flight flag size (window size) (in bytes)",                            OFFSET(fc),               AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
> +    { "ipttl",          "IP Time To Live",                                                      OFFSET(ipttl),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E },
> +    { "iptos",          "IP Type of Service",                                                   OFFSET(iptos),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E },
> +    { "inputbw",        "Estimated input stream rate",                                          OFFSET(inputbw),          AV_OPT_TYPE_INT64,    { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
> +    { "oheadbw",        "MaxBW ceiling based on % over input stream rate",                      OFFSET(oheadbw),          AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 100,       .flags = D|E },
> +    { "tsbpddelay",     "TsbPd receiver delay to absorb burst of missed packet retransmission", OFFSET(tsbpddelay),       AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
> +    { "tlpktdrop",      "Enable receiver pkt drop",                                             OFFSET(tlpktdrop),        AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1,         .flags = D|E },
> +    { "nakreport",      "Enable receiver to send periodic NAK reports",                         OFFSET(nakreport),        AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1,         .flags = D|E },
> +    { "conntimeo",      "Connect timeout. Ccaller default: 3000, rendezvous (x 10)",            OFFSET(conntimeo),        AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
> +    { "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 },
> +    { "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);

AVERROR_EXTERNAL; or even better, map all the error code that can be
mapped.

> +}
> +
> +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;
> +    }

It looks like it will make quite a few system calls. Maybe create eid at
the beginning and reuse it?

> +    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 int opensrt_network_wait_fd(int fd, int write)
> +{
> +    int ev = write ? POLLOUT : POLLIN;
> +    struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
> +    int ret;
> +    ret = opensrt_poll(&p, 1, POLLING_TIME);
> +    return ret < 0 ? opensrt_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN);
> +}

You are wrapping the arguments in a pollfd structure, and then
unwrapping them to pass them to the libsrt API. It looks unnecessary,
and only there because you followed the example of TCP too closely. I
think you should merge opensrt_poll() and this function to use fd
directly with srt_epoll_add_usock().

> +
> +static int opensrt_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb)
> +{
> +    int ret;
> +    int64_t wait_start = 0;
> +
> +    while (1) {
> +        if (ff_check_interrupt(int_cb))
> +            return AVERROR_EXIT;
> +        ret = opensrt_network_wait_fd(fd, write);
> +        if (ret != AVERROR(EAGAIN))
> +            return ret;
> +        if (timeout > 0) {
> +            if (!wait_start)
> +                wait_start = av_gettime_relative();
> +            else if (av_gettime_relative() - wait_start > timeout)
> +                return AVERROR(ETIMEDOUT);
> +        }
> +    }
> +}

This block looks like a duplicate of ff_network_wait_fd_timeout() with
the function changed. It would probably be better to factor the code,
but it is not trivial to do it cleanly.

In the meantime, please add a comment, maybe:

/* TODO de-duplicate code from ff_network_wait_fd_timeout() */

> +
> +static int opensrt_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout, AVIOInterruptCB *cb)
> +{
> +    int runs = timeout / POLLING_TIME;
> +    int ret = 0;
> +
> +    do {
> +        if (ff_check_interrupt(cb))
> +            return AVERROR_EXIT;
> +        ret = opensrt_poll(p, nfds, POLLING_TIME);
> +        if (ret != 0)
> +            break;
> +    } while (timeout <= 0 || runs-- > 0);
> +
> +    if (!ret)
> +        return AVERROR(ETIMEDOUT);
> +    if (ret < 0)
> +        return opensrt_neterrno();
> +    return ret;
> +}

Ditto for ff_poll_interrupt().

> +
> +static int opensrt_do_accept(int fd, int timeout, URLContext *h)
> +{
> +    int ret;
> +    struct pollfd lp = { fd, POLLIN, 0 };
> +
> +    ret = opensrt_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
> +    if (ret < 0)
> +        return ret;
> +
> +    ret = srt_accept(fd, NULL, NULL);
> +    if (ret < 0)
> +        return opensrt_neterrno();
> +    if (opensrt_socket_nonblock(ret, 1) < 0)
> +        av_log(h, AV_LOG_DEBUG, "opensrt_socket_nonblock failed\n");
> +
> +    return ret;
> +}
> +
> +static int opensrt_listen(int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h)
> +{
> +    int ret;
> +    int reuse = 1;
> +    if (srt_setsockopt(fd, SOL_SOCKET, SRTO_REUSEADDR, &reuse, sizeof(reuse))) {
> +        av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_REUSEADDR) failed\n");
> +    }
> +    ret = srt_bind(fd, addr, addrlen);
> +    if (ret)
> +        return opensrt_neterrno();
> +
> +    ret = srt_listen(fd, 1);
> +    if (ret)
> +        return opensrt_neterrno();
> +    return ret;
> +}
> +
> +static int opensrt_listen_connect(int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h, int will_try_next)
> +{
> +    struct pollfd p = {fd, POLLOUT, 0};
> +    int ret;
> +
> +    if (opensrt_socket_nonblock(fd, 1) < 0)
> +        av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
> +
> +    while ((ret = srt_connect(fd, addr, addrlen))) {
> +        ret = opensrt_neterrno();
> +        switch (ret) {
> +        case AVERROR(EINTR):
> +            if (ff_check_interrupt(&h->interrupt_callback))
> +                return AVERROR_EXIT;
> +            continue;
> +        case AVERROR(EINPROGRESS):
> +        case AVERROR(EAGAIN):
> +            ret = opensrt_poll_interrupt(&p, 1, timeout, &h->interrupt_callback);
> +            if (ret < 0)
> +                return ret;
> +            ret = srt_getlasterror(NULL);
> +            srt_clearlasterror();
> +            if (ret != 0) {
> +                char errbuf[100];

> +                ret = AVERROR(ret);
> +                av_strerror(ret, errbuf, sizeof(errbuf));

Use av_err2str().

> +                if (will_try_next)
> +                    av_log(h, AV_LOG_WARNING,
> +                           "Connection to %s failed (%s), trying next address\n",
> +                           h->filename, errbuf);
> +                else
> +                    av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n",
> +                           h->filename, errbuf);
> +            }
> +        default:
> +            return ret;
> +        }
> +    }
> +    return ret;
> +}
> +
> +/* - 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());

Missing \n.

> +        return AVERROR(EIO);

Is it really the best error code for this situation?

> +    }
> +    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);

Ditto.

> +    }
> +    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;
> +    int tsbpddelay = s->tsbpddelay / 1000;
> +    int conntimeo = s->conntimeo;
> +

> +    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 (tsbpddelay >= 0 && srt_setsockopt(fd, 0, SRTO_TSBPDDELAY, &tsbpddelay, sizeof(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 (conntimeo >= 0 && srt_setsockopt(fd, 0, SRTO_CONNTIMEO, &conntimeo, sizeof(conntimeo)) < 0) {
> +        av_log(h, AV_LOG_ERROR, "failed to set option SRTO_CONNTIMEO on socket: %s", srt_getlasterror_str());
> +        return AVERROR(EIO);
> +    }

Please factor that.

> +    return 0;
> +}
> +
> +
> +static int opensrt_setup(URLContext *h, const char *uri, int flags)
> +{
> +    struct addrinfo hints = { 0 }, *ai, *cur_ai;
> +    int port, fd = -1;
> +    SRTContext *s = h->priv_data;
> +    const char *p;
> +    char buf[256];
> +    int ret;
> +    char hostname[1024],proto[1024],path[1024];
> +    char portstr[10];
> +    int open_timeout = 5000000;
> +
> +    av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
> +        &port, path, sizeof(path), uri);
> +    if (strcmp(proto, "srt"))
> +        return AVERROR(EINVAL);
> +    if (port <= 0 || port >= 65536) {
> +        av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
> +        return AVERROR(EINVAL);
> +    }
> +    p = strchr(uri, '?');
> +    if (p) {
> +        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
> +            s->rw_timeout = strtol(buf, NULL, 10);
> +        }
> +        if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
> +            s->listen_timeout = strtol(buf, NULL, 10);
> +        }
> +    }
> +    if (s->rw_timeout >= 0) {
> +        open_timeout = h->rw_timeout = s->rw_timeout;
> +    }
> +    hints.ai_family = AF_UNSPEC;
> +    hints.ai_socktype = SOCK_STREAM;
> +    snprintf(portstr, sizeof(portstr), "%d", port);
> +    if (s->mode == SRT_MODE_LISTENER)
> +        hints.ai_flags |= AI_PASSIVE;
> +    if (!hostname[0])
> +        ret = getaddrinfo(NULL, portstr, &hints, &ai);
> +    else
> +        ret = getaddrinfo(hostname, portstr, &hints, &ai);

getaddrinfo(hostname[0] ? hostname : NULL), maybe?

> +    if (ret) {
> +        av_log(h, AV_LOG_ERROR,
> +               "Failed to resolve hostname %s: %s\n",
> +               hostname, gai_strerror(ret));
> +        return AVERROR(EIO);
> +    }
> +
> +    cur_ai = ai;
> +
> + restart:
> +
> +    fd = srt_socket(cur_ai->ai_family, cur_ai->ai_socktype, 0);
> +    if (fd < 0) {
> +        ret = opensrt_neterrno();
> +        goto fail;
> +    }
> +
> +    if ((ret = opensrt_set_options_pre(h, fd)) < 0) {
> +        goto fail;
> +    }
> +
> +    /* Set the socket's send or receive buffer sizes, if specified.
> +       If unspecified or setting fails, system default is used. */
> +    if (s->recv_buffer_size > 0) {
> +        srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
> +    }
> +    if (s->send_buffer_size > 0) {
> +        srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
> +    }
> +    if (s->mode == SRT_MODE_LISTENER) {
> +        // multi-client
> +        if ((ret = opensrt_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h)) < 0)
> +            goto fail1;
> +    } else {
> +        if ((ret = opensrt_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
> +                                     open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
> +
> +            if (ret == AVERROR_EXIT)
> +                goto fail1;
> +            else
> +                goto fail;
> +        }
> +    }
> +    if ((ret = opensrt_set_options_post(h, fd)) < 0) {
> +        goto fail;
> +    }
> +
> +    h->is_streamed = 1;
> +    s->fd = fd;
> +
> +    freeaddrinfo(ai);
> +    return 0;
> +
> + fail:
> +    if (cur_ai->ai_next) {
> +        /* Retry with the next sockaddr */
> +        cur_ai = cur_ai->ai_next;
> +        if (fd >= 0)
> +            srt_close(fd);
> +        ret = 0;
> +        goto restart;
> +    }
> + fail1:
> +    if (fd >= 0)
> +        srt_close(fd);
> +    freeaddrinfo(ai);
> +    return ret;
> +}
> +
> +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);

AVERROR_EXTERNAL or more accurate translation.

> +    }
> +
> +    /* 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);

Maybe use 0 instead of 10 to allow hex.

> +        }
> +        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;
> +            }

Missing final case.

> +        }
> +    }
> +    return opensrt_setup(h, uri, flags);
> +}
> +
> +
> +static int opensrt_accept(URLContext *s, URLContext **c)
> +{
> +    SRTContext *sc = s->priv_data;
> +    SRTContext *cc;
> +    int ret;
> +    av_assert0(sc->mode == SRT_MODE_LISTENER);
> +    if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0)
> +        return ret;
> +    cc = (*c)->priv_data;
> +    ret = opensrt_do_accept(sc->fd, sc->listen_timeout / 1000, s);
> +    if (ret < 0)
> +        return ret;
> +    cc->fd = ret;
> +    return 0;
> +}
> +
> +static int opensrt_read(URLContext *h, uint8_t *buf, int size)
> +{
> +    SRTContext *s = h->priv_data;
> +    int ret;
> +
> +    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
> +        ret = opensrt_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
> +        if (ret)
> +            return ret;
> +    }
> +    ret = srt_recvmsg(s->fd, buf, size);
> +    return ret < 0 ? opensrt_neterrno() : ret;
> +}
> +
> +static int opensrt_write(URLContext *h, const uint8_t *buf, int size)
> +{
> +    SRTContext *s = h->priv_data;
> +    int ret;
> +
> +    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
> +        ret = opensrt_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
> +        if (ret)
> +            return ret;
> +    }
> +    ret = srt_sendmsg(s->fd, buf, size, -1, 0);
> +    return ret < 0 ? opensrt_neterrno() : ret;
> +}
> +
> +static int opensrt_close(URLContext *h)
> +{
> +    SRTContext *s = h->priv_data;
> +
> +    srt_close(s->fd);
> +
> +    srt_cleanup();
> +
> +    return 0;
> +}
> +
> +static int opensrt_get_file_handle(URLContext *h)
> +{
> +    SRTContext *s = h->priv_data;
> +    return s->fd;
> +}
> +
> +static int opensrt_get_window_size(URLContext *h)
> +{
> +    SRTContext *s = h->priv_data;
> +    int avail;
> +    socklen_t avail_len = sizeof(avail);
> +
> +    if (srt_getsockopt(s->fd, SOL_SOCKET, SRTO_UDP_RCVBUF, &avail, &avail_len)) {
> +        return opensrt_neterrno();
> +    }
> +    return avail;
> +}
> +
> +const URLProtocol ff_opensrt_protocol = {
> +    .name                = "srt",
> +    .url_open            = opensrt_open,
> +    .url_accept          = opensrt_accept,
> +    .url_read            = opensrt_read,
> +    .url_write           = opensrt_write,
> +    .url_close           = opensrt_close,
> +    .url_get_file_handle = opensrt_get_file_handle,
> +    .url_get_short_seek  = opensrt_get_window_size,
> +    .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;

Regards,
Nablet Developer Feb. 12, 2018, 3:34 p.m. UTC | #6
> If you or any other developer is against someone receiving git write access
> you only need to object to the patch adding him to the MAINTAINERS file.
>
> Thats one of the reasons why there is a MAINTAINERs file.
>
> It would be ideal IMO if for each part of teh codebase at least one person
> with write access cares/"Maintains" it. Is there someone who already has
> write access who wants to take care of opensrt.c ?
> (it seemed there is little interrest from teh lack of replies in the thread so
> far)
>
> Also i belive there already is a developer from nablet who is in the
> MAINTAINERs file and has write access.
>
> thanks
>

thanks for your comments. I am going to send new patch later this week, 
as there are comments
from the Nicolas George to be addressed anyway.
it was your suggestion to edit MAINTAINERs file, so I followed it, but 
now I am disappointed.
so what should I do now?
I see the following options:
1) do not edit MAINTAINERs file (leave it as it was before)
2) add opensrt.c entry for existing maintainer from Nablet (Ivan Uskov 
<ivan.uskov@nablet.com>)
3) change opensrt.c entry to another maintainer from Nablet (Sven 
Dueking <sven.dueking@nablet.com>)
which one do you prefer/suggest?
if I change maintainer entry, does it imply what patch should be sent 
from maintainer's email and
commit author should also match maintainer's email?
P.S. added file (opensrt.c) doesn't have a Nablet or Haivision copyright 
- it has regular ffmpeg copyright
used across the ffmpeg codebase, so it's not different in that aspect 
from other files, like tcp.c or udp.c.
diff mbox

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index ba7a728..0317f24 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -498,6 +498,7 @@  Protocols:
   http.c                                Ronald S. Bultje
   libssh.c                              Lukasz Marek
   mms*.c                                Ronald S. Bultje
+  opensrt.c                             Nablet Developer
   udp.c                                 Luca Abeni
   icecast.c                             Marvin Scholz
 
diff --git a/configure b/configure
index fcfa7aa..57705ee 100755
--- a/configure
+++ b/configure
@@ -294,6 +294,7 @@  External library support:
   --enable-opengl          enable OpenGL rendering [no]
   --enable-openssl         enable openssl, needed for https support
                            if gnutls or libtls is not used [no]
+  --enable-opensrt         enable Haivision Open SRT protocol [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]
@@ -1641,6 +1642,7 @@  EXTERNAL_LIBRARY_LIST="
     mediacodec
     openal
     opengl
+    opensrt
 "
 
 HWACCEL_AUTODETECT_LIBRARY_LIST="
@@ -3148,6 +3150,8 @@  libssh_protocol_deps="libssh"
 libtls_conflict="openssl gnutls"
 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"
@@ -5986,6 +5990,7 @@  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           && require_pkg_config libsrt "srt >= 1.2.0" srt/srt.h srt_socket
 enabled openssl           && { check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl ||
                                check_pkg_config openssl openssl openssl/ssl.h SSL_library_init ||
                                check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto ||
@@ -6036,6 +6041,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" "-Wl,-framework,CoreFoundation -Wl,-framework,Security" ||
diff --git a/doc/protocols.texi b/doc/protocols.texi
index 98deb73..2e5e630 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -755,6 +755,122 @@  Set the workgroup used for making connections. By default workgroup is not speci
 
 For more information see: @url{http://www.samba.org/}.
 
+@section srt
+
+Haivision Secure Reliable Transport Protocol via libsrt.
+
+The required syntax for a SRT url is:
+@example
+srt://@var{hostname}:@var{port}[?@var{options}]
+@end example
+
+@var{options} contains a list of &-separated options of the form
+@var{key}=@var{val}.
+
+This protocol accepts the following options.
+
+@table @option
+@item conntimeo
+Connection timeout. SRT cannot connect for RTT > 1500 msec
+(2 handshake exchanges) with the default connect timeout of 3 seconds. This option
+applies to the caller and rendezvous connection modes. The connect timeout is 10 times
+the value set for the rendezvous mode (which can be used as a workaround for this
+connection problem with earlier versions).
+
+@item fc=@var{bytes}
+Flight Flag Size (Window Size), in bytes. FC is actually an internal parameter and
+you should set it to not less than @option{recv_buffer_size} and @option{mss}.
+The default value is relatively large, therefore unless you set a very large
+receiver buffer, you do not need to change this option. Default value is 25600.
+
+@item inputbw=@var{bytes/seconds}
+Sender nominal input rate, in bytes per seconds. Used along with @option{oheadbw},
+when @option{maxbw} is set to relative (0), to calculate maximum sending rate when
+recovery packets are sent along with main media stream:
+@option{inputbw} * (100 + @option{oheadbw}) / 100
+if @option{inputbw} is not set while @option{maxbw} is set to relative (0), the actual
+ctual input rate is evaluated inside the library. Default value is 0.
+
+@item iptos=@var{tos}
+IP Type of Service. Applies to sender only. Default value is 0xB8.
+
+@item ipttl=@var{ttl}
+IP Time To Live. Applies to sender only. Default value is 64.
+
+@item listen_timeout
+Set socket listen timeout.
+
+@item maxbw=@var{bytes/seconds}
+Maximum sending bandwidth, in bytes per seconds.
+-1 infinite (CSRTCC limit is 30mbps)
+0 relative to input rate (see @option{inputbw})
+>0 absolute limit value
+Default value is 0 (relative)
+
+@item mode=@var{caller|listener|rendezvous}
+Connection mode.
+caller opens client connection.
+listener starts server to listen for incoming connections.
+rendezvous use Rendez-Vous connection mode.
+Default valus is caller.
+
+@item mss=@var{bytes}
+Maximum Segment Size, in bytes. Used for buffer allocation and rate calculation using
+packet counter assuming fully filled packets. The smallest MSS between the peers is
+used. This is 1500 by default in the overall internet. This is the maximum size of the
+UDP packet and can be only decreased, unless you have some unusual dedicated network
+settings. Default value is 1500.
+
+@item nakreport=@var{1|0}
+If set to 1, Receiver will send `UMSG_LOSSREPORT` messages periodically until the
+lost packet is retransmitted or intentionally dropped. Default value is 1.
+
+@item oheadbw=@var{percents}
+Recovery bandwidth overhead above input rate, in percents. See @option{inputbw}.
+Default value is 25%.
+
+@item passphrase=@var{string}
+HaiCrypt Encryption/Decryption Passphrase string, length from 10 to 79 characters.
+The passphrase is the shared secret between the sender and the receiver.
+It is used to generate the Key Encrypting Key using PBKDF2 (Password-Based
+Key Deriviation Function). It is used only if @option{pbkeylen} is non-zero.
+t is used on the receiver only if the received data is encrypted.
+The configured passphrase cannot be get back (write-only).
+
+@item pbkeylen=@var{bytes}
+Sender encryption key length, in bytes. Only can be set to 0, 16, 24 and 32.
+Enable sender encryption if not 0. Not required on receiver (set to 0),
+key size obtained from sender in HaiCrypt handshake. Default value is 0.
+
+@item recv_buffer_size=@var{bytes}
+Set receive buffer size, expressed bytes.
+
+@item send_buffer_size=@var{bytes}
+Set send buffer size, expressed bytes.
+
+@item timeout
+Set raise error timeout.
+
+This option is only relevant in read mode: if no data arrived in more
+than this time interval, raise error.
+
+@item tlpktdrop=@var{1|0}
+Too-late Packet Drop. When enabled on receiver, it skips missing packets that
+have not been delivered in time and deliver the following packets to the application
+when their time-to-play has come. It also send a fake ACK to sender. When enabled on
+sender and enabled on the receiving peer, sender drops the older packets that have no
+chance to be delivered in time. It was automatically enabled in sender if receiver
+supports it.
+
+@item tsbpddelay
+Timestamp-based Packet Delivery Delay.
+Used to absorb burst of missed packet retransmission.
+
+@end table
+
+For more information see: @url{https://github.com/Haivision/srt}.
+
+
 @section libssh
 
 Secure File Transfer Protocol via libssh
diff --git a/libavformat/Makefile b/libavformat/Makefile
index de0de92..bd92071 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -598,6 +598,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..0b16391
--- /dev/null
+++ b/libavformat/opensrt.c
@@ -0,0 +1,621 @@ 
+/*
+ * 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
+
+#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 {
+    int fd;
+    int rw_timeout;
+    int listen_timeout;
+    int recv_buffer_size;
+    int send_buffer_size;
+
+    int64_t maxbw;
+    int pbkeylen;
+    char * passphrase;
+    int mss;
+    int fc;
+    int ipttl;
+    int iptos;
+    int64_t inputbw;
+    int 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 OFFSET(x) offsetof(SRTContext, x)
+static const AVOption opensrt_options[] = {
+    { "timeout",        "set timeout of socket I/O operations",                                 OFFSET(rw_timeout),       AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "listen_timeout", "Connection awaiting timeout",                                          OFFSET(listen_timeout),   AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "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 },
+    { "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 },
+    { "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 },
+    { "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 },
+    { "passphrase",     "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto",             OFFSET(passphrase),       AV_OPT_TYPE_STRING,   { .str = NULL },              .flags = D|E },
+    { "mss",            "the Maximum Transfer Unit",                                            OFFSET(mss),              AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1500,      .flags = D|E },
+    { "fc",             "Flight flag size (window size) (in bytes)",                            OFFSET(fc),               AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "ipttl",          "IP Time To Live",                                                      OFFSET(ipttl),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E },
+    { "iptos",          "IP Type of Service",                                                   OFFSET(iptos),            AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 255,       .flags = D|E },
+    { "inputbw",        "Estimated input stream rate",                                          OFFSET(inputbw),          AV_OPT_TYPE_INT64,    { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
+    { "oheadbw",        "MaxBW ceiling based on % over input stream rate",                      OFFSET(oheadbw),          AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 100,       .flags = D|E },
+    { "tsbpddelay",     "TsbPd receiver delay to absorb burst of missed packet retransmission", OFFSET(tsbpddelay),       AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,   .flags = D|E },
+    { "tlpktdrop",      "Enable receiver pkt drop",                                             OFFSET(tlpktdrop),        AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1,         .flags = D|E },
+    { "nakreport",      "Enable receiver to send periodic NAK reports",                         OFFSET(nakreport),        AV_OPT_TYPE_INT,      { .i64 = -1 }, -1, 1,         .flags = D|E },
+    { "conntimeo",      "Connect timeout. Ccaller default: 3000, rendezvous (x 10)",            OFFSET(conntimeo),        AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E },
+    { "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 },
+    { "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 int opensrt_network_wait_fd(int fd, int write)
+{
+    int ev = write ? POLLOUT : POLLIN;
+    struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
+    int ret;
+    ret = opensrt_poll(&p, 1, POLLING_TIME);
+    return ret < 0 ? opensrt_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN);
+}
+
+static int opensrt_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb)
+{
+    int ret;
+    int64_t wait_start = 0;
+
+    while (1) {
+        if (ff_check_interrupt(int_cb))
+            return AVERROR_EXIT;
+        ret = opensrt_network_wait_fd(fd, write);
+        if (ret != AVERROR(EAGAIN))
+            return ret;
+        if (timeout > 0) {
+            if (!wait_start)
+                wait_start = av_gettime_relative();
+            else if (av_gettime_relative() - wait_start > timeout)
+                return AVERROR(ETIMEDOUT);
+        }
+    }
+}
+
+static int opensrt_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout, AVIOInterruptCB *cb)
+{
+    int runs = timeout / POLLING_TIME;
+    int ret = 0;
+
+    do {
+        if (ff_check_interrupt(cb))
+            return AVERROR_EXIT;
+        ret = opensrt_poll(p, nfds, POLLING_TIME);
+        if (ret != 0)
+            break;
+    } while (timeout <= 0 || runs-- > 0);
+
+    if (!ret)
+        return AVERROR(ETIMEDOUT);
+    if (ret < 0)
+        return opensrt_neterrno();
+    return ret;
+}
+
+static int opensrt_do_accept(int fd, int timeout, URLContext *h)
+{
+    int ret;
+    struct pollfd lp = { fd, POLLIN, 0 };
+
+    ret = opensrt_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
+    if (ret < 0)
+        return ret;
+
+    ret = srt_accept(fd, NULL, NULL);
+    if (ret < 0)
+        return opensrt_neterrno();
+    if (opensrt_socket_nonblock(ret, 1) < 0)
+        av_log(h, AV_LOG_DEBUG, "opensrt_socket_nonblock failed\n");
+
+    return ret;
+}
+
+static int opensrt_listen(int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h)
+{
+    int ret;
+    int reuse = 1;
+    if (srt_setsockopt(fd, SOL_SOCKET, SRTO_REUSEADDR, &reuse, sizeof(reuse))) {
+        av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_REUSEADDR) failed\n");
+    }
+    ret = srt_bind(fd, addr, addrlen);
+    if (ret)
+        return opensrt_neterrno();
+
+    ret = srt_listen(fd, 1);
+    if (ret)
+        return opensrt_neterrno();
+    return ret;
+}
+
+static int opensrt_listen_connect(int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h, int will_try_next)
+{
+    struct pollfd p = {fd, POLLOUT, 0};
+    int ret;
+
+    if (opensrt_socket_nonblock(fd, 1) < 0)
+        av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
+
+    while ((ret = srt_connect(fd, addr, addrlen))) {
+        ret = opensrt_neterrno();
+        switch (ret) {
+        case AVERROR(EINTR):
+            if (ff_check_interrupt(&h->interrupt_callback))
+                return AVERROR_EXIT;
+            continue;
+        case AVERROR(EINPROGRESS):
+        case AVERROR(EAGAIN):
+            ret = opensrt_poll_interrupt(&p, 1, timeout, &h->interrupt_callback);
+            if (ret < 0)
+                return ret;
+            ret = srt_getlasterror(NULL);
+            srt_clearlasterror();
+            if (ret != 0) {
+                char errbuf[100];
+                ret = AVERROR(ret);
+                av_strerror(ret, errbuf, sizeof(errbuf));
+                if (will_try_next)
+                    av_log(h, AV_LOG_WARNING,
+                           "Connection to %s failed (%s), trying next address\n",
+                           h->filename, errbuf);
+                else
+                    av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n",
+                           h->filename, errbuf);
+            }
+        default:
+            return ret;
+        }
+    }
+    return ret;
+}
+
+/* - 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;
+    int tsbpddelay = s->tsbpddelay / 1000;
+    int conntimeo = s->conntimeo;
+
+    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 (tsbpddelay >= 0 && srt_setsockopt(fd, 0, SRTO_TSBPDDELAY, &tsbpddelay, sizeof(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 (conntimeo >= 0 && srt_setsockopt(fd, 0, SRTO_CONNTIMEO, &conntimeo, sizeof(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_setup(URLContext *h, const char *uri, int flags)
+{
+    struct addrinfo hints = { 0 }, *ai, *cur_ai;
+    int port, fd = -1;
+    SRTContext *s = h->priv_data;
+    const char *p;
+    char buf[256];
+    int ret;
+    char hostname[1024],proto[1024],path[1024];
+    char portstr[10];
+    int open_timeout = 5000000;
+
+    av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
+        &port, path, sizeof(path), uri);
+    if (strcmp(proto, "srt"))
+        return AVERROR(EINVAL);
+    if (port <= 0 || port >= 65536) {
+        av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
+        return AVERROR(EINVAL);
+    }
+    p = strchr(uri, '?');
+    if (p) {
+        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
+            s->rw_timeout = strtol(buf, NULL, 10);
+        }
+        if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
+            s->listen_timeout = strtol(buf, NULL, 10);
+        }
+    }
+    if (s->rw_timeout >= 0) {
+        open_timeout = h->rw_timeout = s->rw_timeout;
+    }
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    snprintf(portstr, sizeof(portstr), "%d", port);
+    if (s->mode == SRT_MODE_LISTENER)
+        hints.ai_flags |= AI_PASSIVE;
+    if (!hostname[0])
+        ret = getaddrinfo(NULL, portstr, &hints, &ai);
+    else
+        ret = getaddrinfo(hostname, portstr, &hints, &ai);
+    if (ret) {
+        av_log(h, AV_LOG_ERROR,
+               "Failed to resolve hostname %s: %s\n",
+               hostname, gai_strerror(ret));
+        return AVERROR(EIO);
+    }
+
+    cur_ai = ai;
+
+ restart:
+
+    fd = srt_socket(cur_ai->ai_family, cur_ai->ai_socktype, 0);
+    if (fd < 0) {
+        ret = opensrt_neterrno();
+        goto fail;
+    }
+
+    if ((ret = opensrt_set_options_pre(h, fd)) < 0) {
+        goto fail;
+    }
+
+    /* Set the socket's send or receive buffer sizes, if specified.
+       If unspecified or setting fails, system default is used. */
+    if (s->recv_buffer_size > 0) {
+        srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
+    }
+    if (s->send_buffer_size > 0) {
+        srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
+    }
+    if (s->mode == SRT_MODE_LISTENER) {
+        // multi-client
+        if ((ret = opensrt_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h)) < 0)
+            goto fail1;
+    } else {
+        if ((ret = opensrt_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
+                                     open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
+
+            if (ret == AVERROR_EXIT)
+                goto fail1;
+            else
+                goto fail;
+        }
+    }
+    if ((ret = opensrt_set_options_post(h, fd)) < 0) {
+        goto fail;
+    }
+
+    h->is_streamed = 1;
+    s->fd = fd;
+
+    freeaddrinfo(ai);
+    return 0;
+
+ fail:
+    if (cur_ai->ai_next) {
+        /* Retry with the next sockaddr */
+        cur_ai = cur_ai->ai_next;
+        if (fd >= 0)
+            srt_close(fd);
+        ret = 0;
+        goto restart;
+    }
+ fail1:
+    if (fd >= 0)
+        srt_close(fd);
+    freeaddrinfo(ai);
+    return ret;
+}
+
+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);
+    }
+
+    /* 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 opensrt_setup(h, uri, flags);
+}
+
+
+static int opensrt_accept(URLContext *s, URLContext **c)
+{
+    SRTContext *sc = s->priv_data;
+    SRTContext *cc;
+    int ret;
+    av_assert0(sc->mode == SRT_MODE_LISTENER);
+    if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0)
+        return ret;
+    cc = (*c)->priv_data;
+    ret = opensrt_do_accept(sc->fd, sc->listen_timeout / 1000, s);
+    if (ret < 0)
+        return ret;
+    cc->fd = ret;
+    return 0;
+}
+
+static int opensrt_read(URLContext *h, uint8_t *buf, int size)
+{
+    SRTContext *s = h->priv_data;
+    int ret;
+
+    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
+        ret = opensrt_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
+        if (ret)
+            return ret;
+    }
+    ret = srt_recvmsg(s->fd, buf, size);
+    return ret < 0 ? opensrt_neterrno() : ret;
+}
+
+static int opensrt_write(URLContext *h, const uint8_t *buf, int size)
+{
+    SRTContext *s = h->priv_data;
+    int ret;
+
+    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
+        ret = opensrt_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
+        if (ret)
+            return ret;
+    }
+    ret = srt_sendmsg(s->fd, buf, size, -1, 0);
+    return ret < 0 ? opensrt_neterrno() : ret;
+}
+
+static int opensrt_close(URLContext *h)
+{
+    SRTContext *s = h->priv_data;
+
+    srt_close(s->fd);
+
+    srt_cleanup();
+
+    return 0;
+}
+
+static int opensrt_get_file_handle(URLContext *h)
+{
+    SRTContext *s = h->priv_data;
+    return s->fd;
+}
+
+static int opensrt_get_window_size(URLContext *h)
+{
+    SRTContext *s = h->priv_data;
+    int avail;
+    socklen_t avail_len = sizeof(avail);
+
+    if (srt_getsockopt(s->fd, SOL_SOCKET, SRTO_UDP_RCVBUF, &avail, &avail_len)) {
+        return opensrt_neterrno();
+    }
+    return avail;
+}
+
+const URLProtocol ff_opensrt_protocol = {
+    .name                = "srt",
+    .url_open            = opensrt_open,
+    .url_accept          = opensrt_accept,
+    .url_read            = opensrt_read,
+    .url_write           = opensrt_write,
+    .url_close           = opensrt_close,
+    .url_get_file_handle = opensrt_get_file_handle,
+    .url_get_short_seek  = opensrt_get_window_size,
+    .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;