diff mbox series

[FFmpeg-devel] libavformat: add librist protocol

Message ID 20210226202854.25163-1-onemda@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel] libavformat: add librist protocol
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Paul B Mahol Feb. 26, 2021, 8:28 p.m. UTC
This work is sponsored by Open Broadcast Systems.

Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 configure               |   5 +
 doc/protocols.texi      |  29 +++++
 libavformat/Makefile    |   1 +
 libavformat/librist.c   | 248 ++++++++++++++++++++++++++++++++++++++++
 libavformat/protocols.c |   1 +
 5 files changed, 284 insertions(+)
 create mode 100644 libavformat/librist.c

Comments

Marton Balint Feb. 26, 2021, 8:53 p.m. UTC | #1
On Fri, 26 Feb 2021, Paul B Mahol wrote:

> This work is sponsored by Open Broadcast Systems.
>
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
> configure               |   5 +
> doc/protocols.texi      |  29 +++++
> libavformat/Makefile    |   1 +
> libavformat/librist.c   | 248 ++++++++++++++++++++++++++++++++++++++++
> libavformat/protocols.c |   1 +
> 5 files changed, 284 insertions(+)
> create mode 100644 libavformat/librist.c
>

> +#define D AV_OPT_FLAG_DECODING_PARAM
> +#define E AV_OPT_FLAG_ENCODING_PARAM
> +#define OFFSET(x) offsetof(RISTContext, x)
> +static const AVOption librist_options[] = {
> +    { "rist_profile","set profile",     OFFSET(profile),     AV_OPT_TYPE_INT,   {.i64=RIST_PROFILE_MAIN},     0, 2, .flags = D|E, "profile" },
> +    { "simple",      NULL,              0,                   AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_SIMPLE},   0, 0, .flags = D|E, "profile" },
> +    { "main",        NULL,              0,                   AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN},     0, 0, .flags = D|E, "profile" },
> +    { "advanced",    NULL,              0,                   AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E, "profile" },
> +    { "buffer_size", "set buffer_size", OFFSET(buffer_size), AV_OPT_TYPE_INT,   {.i64=0},                     0, INT_MAX, .flags = D|E },
> +    { "log_level",   "set loglevel",    OFFSET(log_level),   AV_OPT_TYPE_INT,   {.i64=-1},                   -1, INT_MAX, .flags = D|E },
> +    { "secret", "set encryption secret",OFFSET(secret),      AV_OPT_TYPE_STRING,{.str=NULL},                  0, 0,       .flags = D|E },
> +    { "encryption","set encryption type",OFFSET(encryption), AV_OPT_TYPE_INT   ,{.i64=0},                     0, INT_MAX, .flags = D|E },

Why have you removed pkt_size? For writing, it is surely useful to be 
configurable, please put it back with -1 as default, so different default 
can be used for read and write. And for writing, the default of 1316 
should be used, as we typically want to pump mpegts into it, split on 
packet boundaries and want no fragmentation.

> +static int librist_open(URLContext *h, const char *uri, int flags)
> +{
> +    RISTContext *s = h->priv_data;
> +    struct rist_logging_settings *logging_settings = &s->logging_settings;
> +    struct rist_peer_config *peer_config = &s->peer_config;
> +    int ret;
> +
> +    if ((flags & (AVIO_FLAG_WRITE | AVIO_FLAG_READ)) == (AVIO_FLAG_WRITE | AVIO_FLAG_READ))
> +        return AVERROR(EINVAL);

AVIO_FLAG_READ_WRITE

> +
> +    return risterr2ret(ret);
> +}
> +
> +static int librist_read(URLContext *h, uint8_t *buf, int size)
> +{
> +    RISTContext *s = h->priv_data;
> +    int available_size = size;
> +    int queue_size = 0;
> +    int offset = 0;
> +
> +    do {
> +        const struct rist_data_block *data_block;
> +        int ret;
> +
> +        ret = rist_receiver_data_read(s->ctx, &data_block, offset == 0 ? POLLING_TIME : 0);
> +        if (ret < 0)
> +            return risterr2ret(ret);
> +
> +        if (ret == 0 || data_block->payload_len <= 0)
> +            break;
> +
> +        queue_size = ret;
> +        av_assert0(data_block->payload_len <= available_size);
> +
> +        size = FFMIN(available_size, data_block->payload_len);
> +        memcpy(buf + offset, data_block->payload, size);
> +        offset += size;
> +        available_size -= size;
> +        rist_receiver_data_block_free((struct rist_data_block**)&data_block);
> +        queue_size--;

No, read one packet in one read call, do not merge packets. Protocols 
don't merge packets. UDP does not merge packets. If this is some 
workaround for librist dropping packets then figure out the bug causing 
that. If UDP works, librist should work too, even without merging packets.

Regards,
Marton
Paul B Mahol Feb. 28, 2021, 7:46 p.m. UTC | #2
On Fri, Feb 26, 2021 at 9:54 PM Marton Balint <cus@passwd.hu> wrote:

>
>
> On Fri, 26 Feb 2021, Paul B Mahol wrote:
>
> > This work is sponsored by Open Broadcast Systems.
> >
> > Signed-off-by: Paul B Mahol <onemda@gmail.com>
> > ---
> > configure               |   5 +
> > doc/protocols.texi      |  29 +++++
> > libavformat/Makefile    |   1 +
> > libavformat/librist.c   | 248 ++++++++++++++++++++++++++++++++++++++++
> > libavformat/protocols.c |   1 +
> > 5 files changed, 284 insertions(+)
> > create mode 100644 libavformat/librist.c
> >
>
> > +#define D AV_OPT_FLAG_DECODING_PARAM
> > +#define E AV_OPT_FLAG_ENCODING_PARAM
> > +#define OFFSET(x) offsetof(RISTContext, x)
> > +static const AVOption librist_options[] = {
> > +    { "rist_profile","set profile",     OFFSET(profile),
>  AV_OPT_TYPE_INT,   {.i64=RIST_PROFILE_MAIN},     0, 2, .flags = D|E,
> "profile" },
> > +    { "simple",      NULL,              0,
>  AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_SIMPLE},   0, 0, .flags = D|E,
> "profile" },
> > +    { "main",        NULL,              0,
>  AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN},     0, 0, .flags = D|E,
> "profile" },
> > +    { "advanced",    NULL,              0,
>  AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E,
> "profile" },
> > +    { "buffer_size", "set buffer_size", OFFSET(buffer_size),
> AV_OPT_TYPE_INT,   {.i64=0},                     0, INT_MAX, .flags = D|E },
> > +    { "log_level",   "set loglevel",    OFFSET(log_level),
>  AV_OPT_TYPE_INT,   {.i64=-1},                   -1, INT_MAX, .flags = D|E
> },
> > +    { "secret", "set encryption secret",OFFSET(secret),
> AV_OPT_TYPE_STRING,{.str=NULL},                  0, 0,       .flags = D|E },
> > +    { "encryption","set encryption type",OFFSET(encryption),
> AV_OPT_TYPE_INT   ,{.i64=0},                     0, INT_MAX, .flags = D|E },
>
> Why have you removed pkt_size? For writing, it is surely useful to be
> configurable, please put it back with -1 as default, so different default
> can be used for read and write. And for writing, the default of 1316
> should be used, as we typically want to pump mpegts into it, split on
> packet boundaries and want no fragmentation.
>

This options makes no sense for read/receive end.


>
> > +static int librist_open(URLContext *h, const char *uri, int flags)
> > +{
> > +    RISTContext *s = h->priv_data;
> > +    struct rist_logging_settings *logging_settings =
> &s->logging_settings;
> > +    struct rist_peer_config *peer_config = &s->peer_config;
> > +    int ret;
> > +
> > +    if ((flags & (AVIO_FLAG_WRITE | AVIO_FLAG_READ)) ==
> (AVIO_FLAG_WRITE | AVIO_FLAG_READ))
> > +        return AVERROR(EINVAL);
>
> AVIO_FLAG_READ_WRITE
>
> > +
> > +    return risterr2ret(ret);
> > +}
> > +
> > +static int librist_read(URLContext *h, uint8_t *buf, int size)
> > +{
> > +    RISTContext *s = h->priv_data;
> > +    int available_size = size;
> > +    int queue_size = 0;
> > +    int offset = 0;
> > +
> > +    do {
> > +        const struct rist_data_block *data_block;
> > +        int ret;
> > +
> > +        ret = rist_receiver_data_read(s->ctx, &data_block, offset == 0
> ? POLLING_TIME : 0);
> > +        if (ret < 0)
> > +            return risterr2ret(ret);
> > +
> > +        if (ret == 0 || data_block->payload_len <= 0)
> > +            break;
> > +
> > +        queue_size = ret;
> > +        av_assert0(data_block->payload_len <= available_size);
> > +
> > +        size = FFMIN(available_size, data_block->payload_len);
> > +        memcpy(buf + offset, data_block->payload, size);
> > +        offset += size;
> > +        available_size -= size;
> > +        rist_receiver_data_block_free((struct
> rist_data_block**)&data_block);
> > +        queue_size--;
>
> No, read one packet in one read call, do not merge packets. Protocols
> don't merge packets. UDP does not merge packets. If this is some
> workaround for librist dropping packets then figure out the bug causing
> that. If UDP works, librist should work too, even without merging packets.
>
> Regards,
> Marton
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Marton Balint Feb. 28, 2021, 9:49 p.m. UTC | #3
On Sun, 28 Feb 2021, Paul B Mahol wrote:

> On Fri, Feb 26, 2021 at 9:54 PM Marton Balint <cus@passwd.hu> wrote:
>
>>
>>
>> On Fri, 26 Feb 2021, Paul B Mahol wrote:
>>
>> > This work is sponsored by Open Broadcast Systems.
>> >
>> > Signed-off-by: Paul B Mahol <onemda@gmail.com>
>> > ---
>> > configure               |   5 +
>> > doc/protocols.texi      |  29 +++++
>> > libavformat/Makefile    |   1 +
>> > libavformat/librist.c   | 248 ++++++++++++++++++++++++++++++++++++++++
>> > libavformat/protocols.c |   1 +
>> > 5 files changed, 284 insertions(+)
>> > create mode 100644 libavformat/librist.c
>> >
>>
>> > +#define D AV_OPT_FLAG_DECODING_PARAM
>> > +#define E AV_OPT_FLAG_ENCODING_PARAM
>> > +#define OFFSET(x) offsetof(RISTContext, x)
>> > +static const AVOption librist_options[] = {
>> > +    { "rist_profile","set profile",     OFFSET(profile),
>>  AV_OPT_TYPE_INT,   {.i64=RIST_PROFILE_MAIN},     0, 2, .flags = D|E,
>> "profile" },
>> > +    { "simple",      NULL,              0,
>>  AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_SIMPLE},   0, 0, .flags = D|E,
>> "profile" },
>> > +    { "main",        NULL,              0,
>>  AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN},     0, 0, .flags = D|E,
>> "profile" },
>> > +    { "advanced",    NULL,              0,
>>  AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E,
>> "profile" },
>> > +    { "buffer_size", "set buffer_size", OFFSET(buffer_size),
>> AV_OPT_TYPE_INT,   {.i64=0},                     0, INT_MAX, .flags = D|E },
>> > +    { "log_level",   "set loglevel",    OFFSET(log_level),
>>  AV_OPT_TYPE_INT,   {.i64=-1},                   -1, INT_MAX, .flags = D|E
>> },
>> > +    { "secret", "set encryption secret",OFFSET(secret),
>> AV_OPT_TYPE_STRING,{.str=NULL},                  0, 0,       .flags = D|E },
>> > +    { "encryption","set encryption type",OFFSET(encryption),
>> AV_OPT_TYPE_INT   ,{.i64=0},                     0, INT_MAX, .flags = D|E },
>>
>> Why have you removed pkt_size? For writing, it is surely useful to be
>> configurable, please put it back with -1 as default, so different default
>> can be used for read and write. And for writing, the default of 1316
>> should be used, as we typically want to pump mpegts into it, split on
>> packet boundaries and want no fragmentation.
>>
>
> This options makes no sense for read/receive end.

Then use the value set by the option for the write/send case only.

Regards,
Marton
diff mbox series

Patch

diff --git a/configure b/configure
index d11942fced..378b1e0cc2 100755
--- a/configure
+++ b/configure
@@ -259,6 +259,7 @@  External library support:
   --enable-libpulse        enable Pulseaudio input via libpulse [no]
   --enable-librabbitmq     enable RabbitMQ library [no]
   --enable-librav1e        enable AV1 encoding via rav1e [no]
+  --enable-librist         enable RIST via librist [no]
   --enable-librsvg         enable SVG rasterization via librsvg [no]
   --enable-librubberband   enable rubberband needed for rubberband filter [no]
   --enable-librtmp         enable RTMP[E] support via librtmp [no]
@@ -1797,6 +1798,7 @@  EXTERNAL_LIBRARY_LIST="
     libpulse
     librabbitmq
     librav1e
+    librist
     librsvg
     librtmp
     libshine
@@ -3490,6 +3492,8 @@  unix_protocol_select="network"
 # external library protocols
 libamqp_protocol_deps="librabbitmq"
 libamqp_protocol_select="network"
+librist_protocol_deps="librist"
+librist_protocol_select="network"
 librtmp_protocol_deps="librtmp"
 librtmpe_protocol_deps="librtmp"
 librtmps_protocol_deps="librtmp"
@@ -6409,6 +6413,7 @@  enabled libopus           && {
 enabled libpulse          && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new
 enabled librabbitmq       && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection
 enabled librav1e          && require_pkg_config librav1e "rav1e >= 0.4.0" rav1e.h rav1e_context_new
+enabled librist           && require_pkg_config librist "librist >= 0.2" librist/librist.h rist_receiver_create
 enabled librsvg           && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_render_cairo
 enabled librtmp           && require_pkg_config librtmp librtmp librtmp/rtmp.h RTMP_Socket
 enabled librubberband     && require_pkg_config librubberband "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new -lstdc++ && append librubberband_extralibs "-lstdc++"
diff --git a/doc/protocols.texi b/doc/protocols.texi
index c0b511b7a4..11fb351bf6 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -690,6 +690,35 @@  Example usage:
 -f rtp_mpegts -fec prompeg=l=8:d=4 rtp://@var{hostname}:@var{port}
 @end example
 
+@section rist
+
+Reliable Internet Streaming Transport protocol
+
+The accepted options are:
+@table @option
+@item rist_profile
+Supported values:
+@table @samp
+@item simple
+@item main
+This one is default.
+@item advanced
+@end table
+
+@item buffer_size
+Set internal RIST buffer size for retransmission of data.
+
+@item log_level
+Set loglevel for RIST logging messages.
+
+@item secret
+Set override of encryption secret, by default is unset.
+
+@item encryption
+Set encryption type, by default is disabled.
+Acceptable values are 128 and 256.
+@end table
+
 @section rtmp
 
 Real-Time Messaging Protocol.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 56fa96ea7f..95ed25e866 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -654,6 +654,7 @@  OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o
 
 # external library protocols
 OBJS-$(CONFIG_LIBAMQP_PROTOCOL)          += libamqp.o
+OBJS-$(CONFIG_LIBRIST_PROTOCOL)          += librist.o
 OBJS-$(CONFIG_LIBRTMP_PROTOCOL)          += librtmp.o
 OBJS-$(CONFIG_LIBRTMPE_PROTOCOL)         += librtmp.o
 OBJS-$(CONFIG_LIBRTMPS_PROTOCOL)         += librtmp.o
diff --git a/libavformat/librist.c b/libavformat/librist.c
new file mode 100644
index 0000000000..f2b17b946b
--- /dev/null
+++ b/libavformat/librist.c
@@ -0,0 +1,248 @@ 
+/*
+ * 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
+ * Reliable Internet Streaming Transport protocol
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/time.h"
+
+#include "avformat.h"
+#include "internal.h"
+#include "network.h"
+#include "os_support.h"
+#include "url.h"
+
+#include <librist/librist.h>
+
+typedef struct RISTContext {
+    const AVClass *class;
+
+    int profile;
+    int buffer_size;
+    int log_level;
+    int encryption;
+    char *secret;
+
+    struct rist_logging_settings logging_settings;
+    struct rist_peer_config peer_config;
+
+    struct rist_peer *peer;
+    struct rist_ctx *ctx;
+} RISTContext;
+
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+#define OFFSET(x) offsetof(RISTContext, x)
+static const AVOption librist_options[] = {
+    { "rist_profile","set profile",     OFFSET(profile),     AV_OPT_TYPE_INT,   {.i64=RIST_PROFILE_MAIN},     0, 2, .flags = D|E, "profile" },
+    { "simple",      NULL,              0,                   AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_SIMPLE},   0, 0, .flags = D|E, "profile" },
+    { "main",        NULL,              0,                   AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN},     0, 0, .flags = D|E, "profile" },
+    { "advanced",    NULL,              0,                   AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E, "profile" },
+    { "buffer_size", "set buffer_size", OFFSET(buffer_size), AV_OPT_TYPE_INT,   {.i64=0},                     0, INT_MAX, .flags = D|E },
+    { "log_level",   "set loglevel",    OFFSET(log_level),   AV_OPT_TYPE_INT,   {.i64=-1},                   -1, INT_MAX, .flags = D|E },
+    { "secret", "set encryption secret",OFFSET(secret),      AV_OPT_TYPE_STRING,{.str=NULL},                  0, 0,       .flags = D|E },
+    { "encryption","set encryption type",OFFSET(encryption), AV_OPT_TYPE_INT   ,{.i64=0},                     0, INT_MAX, .flags = D|E },
+    { NULL }
+};
+
+static int risterr2ret(int err)
+{
+    switch (err) {
+    case RIST_ERR_MALLOC:
+        return AVERROR(ENOMEM);
+    default:
+        return AVERROR_EXTERNAL;
+    }
+}
+
+static int log_cb(void *arg, enum rist_log_level log_level, const char *msg)
+{
+    int level;
+
+    switch (log_level) {
+    case RIST_LOG_ERROR:    level = AV_LOG_ERROR;   break;
+    case RIST_LOG_WARN:     level = AV_LOG_WARNING; break;
+    case RIST_LOG_NOTICE:   level = AV_LOG_VERBOSE; break;
+    case RIST_LOG_INFO:     level = AV_LOG_INFO;    break;
+    case RIST_LOG_DEBUG:    level = AV_LOG_DEBUG;   break;
+    case RIST_LOG_DISABLE:  level = AV_LOG_QUIET;   break;
+    case RIST_LOG_SIMULATE: level = AV_LOG_TRACE;   break;
+    default: level = AV_LOG_PANIC;
+    }
+
+    av_log(arg, level, "%s", msg);
+
+    return 0;
+}
+
+static int librist_close(URLContext *h)
+{
+    RISTContext *s = h->priv_data;
+    int ret = 0;
+
+    s->peer = NULL;
+
+    if (s->ctx)
+        ret = rist_destroy(s->ctx);
+    s->ctx = NULL;
+
+    return risterr2ret(ret);
+}
+
+static int librist_open(URLContext *h, const char *uri, int flags)
+{
+    RISTContext *s = h->priv_data;
+    struct rist_logging_settings *logging_settings = &s->logging_settings;
+    struct rist_peer_config *peer_config = &s->peer_config;
+    int ret;
+
+    if ((flags & (AVIO_FLAG_WRITE | AVIO_FLAG_READ)) == (AVIO_FLAG_WRITE | AVIO_FLAG_READ))
+        return AVERROR(EINVAL);
+
+    ret = rist_logging_set(&logging_settings, s->log_level, log_cb, h, NULL, NULL);
+    if (ret < 0)
+        return risterr2ret(ret);
+
+    if (flags & AVIO_FLAG_WRITE)
+        ret = rist_sender_create(&s->ctx, s->profile, 0, logging_settings);
+    if (ret < 0)
+        goto err;
+
+    if (flags & AVIO_FLAG_READ)
+        ret = rist_receiver_create(&s->ctx, s->profile, logging_settings);
+    if (ret < 0)
+        goto err;
+
+    ret = rist_peer_config_defaults_set(peer_config);
+    if (ret < 0)
+        goto err;
+
+    ret = rist_parse_address(uri, (const struct rist_peer_config **)&peer_config);
+    if (ret < 0)
+        goto err;
+
+    if (((s->encryption == 128 || s->encryption == 256) && !s->secret) ||
+        ((peer_config->key_size == 128 || peer_config->key_size == 256) && !peer_config->secret[0])) {
+        av_log(h, AV_LOG_ERROR, "secret is mandatory if encryption is enabled\n");
+        librist_close(h);
+        return AVERROR(EINVAL);
+    }
+
+    if (s->secret && peer_config->secret[0] == 0)
+        av_strlcpy(peer_config->secret, s->secret, FFMIN(RIST_MAX_STRING_SHORT - 1, strlen(s->secret)));
+
+    if (s->secret && (s->encryption == 128 || s->encryption == 256))
+        peer_config->key_size = s->encryption;
+
+    if (s->buffer_size) {
+        peer_config->recovery_length_min = s->buffer_size;
+        peer_config->recovery_length_max = s->buffer_size;
+    }
+
+    ret = rist_peer_create(s->ctx, &s->peer, &s->peer_config);
+    if (ret < 0)
+        goto err;
+
+    ret = rist_start(s->ctx);
+    if (ret < 0)
+        goto err;
+
+    if (flags & AVIO_FLAG_WRITE)
+        h->max_packet_size = 9968;
+    else
+        h->max_packet_size = 1 << 20;
+
+    return 0;
+
+err:
+    librist_close(h);
+
+    return risterr2ret(ret);
+}
+
+static int librist_read(URLContext *h, uint8_t *buf, int size)
+{
+    RISTContext *s = h->priv_data;
+    int available_size = size;
+    int queue_size = 0;
+    int offset = 0;
+
+    do {
+        const struct rist_data_block *data_block;
+        int ret;
+
+        ret = rist_receiver_data_read(s->ctx, &data_block, offset == 0 ? POLLING_TIME : 0);
+        if (ret < 0)
+            return risterr2ret(ret);
+
+        if (ret == 0 || data_block->payload_len <= 0)
+            break;
+
+        queue_size = ret;
+        av_assert0(data_block->payload_len <= available_size);
+
+        size = FFMIN(available_size, data_block->payload_len);
+        memcpy(buf + offset, data_block->payload, size);
+        offset += size;
+        available_size -= size;
+        rist_receiver_data_block_free((struct rist_data_block**)&data_block);
+        queue_size--;
+    } while (queue_size > 0 && available_size >= 9968);
+
+    return offset;
+}
+
+static int librist_write(URLContext *h, const uint8_t *buf, int size)
+{
+    RISTContext *s = h->priv_data;
+    struct rist_data_block data_block = { 0 };
+    int ret;
+
+    data_block.ts_ntp = 0;
+    data_block.payload = buf;
+    data_block.payload_len = size;
+
+    ret = rist_sender_data_write(s->ctx, &data_block);
+    if (ret < 0)
+        return risterr2ret(ret);
+
+    return ret;
+}
+
+static const AVClass librist_class = {
+    .class_name = "librist",
+    .item_name  = av_default_item_name,
+    .option     = librist_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_librist_protocol = {
+    .name                = "rist",
+    .url_open            = librist_open,
+    .url_read            = librist_read,
+    .url_write           = librist_write,
+    .url_close           = librist_close,
+    .priv_data_size      = sizeof(RISTContext),
+    .flags               = URL_PROTOCOL_FLAG_NETWORK,
+    .priv_data_class     = &librist_class,
+};
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index 7df18fbb3b..c4fc446bb6 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -61,6 +61,7 @@  extern const URLProtocol ff_udp_protocol;
 extern const URLProtocol ff_udplite_protocol;
 extern const URLProtocol ff_unix_protocol;
 extern const URLProtocol ff_libamqp_protocol;
+extern const URLProtocol ff_librist_protocol;
 extern const URLProtocol ff_librtmp_protocol;
 extern const URLProtocol ff_librtmpe_protocol;
 extern const URLProtocol ff_librtmps_protocol;