[FFmpeg-devel] [ONLY FOR TEST PATCH 2/2] avformat/rtmp: add rtmp over srt

Thomas Volkert silvo at gmx.net
Tue May 18 19:19:13 EEST 2021


Hi,

from my understand "ffrtmpsrt" combines rtmp (with flv (de-)muxing),
which demands by design for a transmission without packet loss, with a
transmission protocol based on UDP, which can have packet loss. So, I am
not sure if we actually should add something like this to our source code.

Best regards,
Thomas.

On 18.05.2021 18:03, Zhao Zhili wrote:
> ---
> Test with:
> ./ffplay -listen 1 rtmpsrt://127.0.0.1:8888
> ./ffmpeg -re -i bunny.mp4 -c copy -f flv rtmpsrt://127.0.0.1:8888
>
>  configure               |   2 +
>  libavformat/Makefile    |   2 +
>  libavformat/protocols.c |   2 +
>  libavformat/rtmpproto.c |  11 ++-
>  libavformat/rtmpsrt.c   | 167 ++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 183 insertions(+), 1 deletion(-)
>  create mode 100644 libavformat/rtmpsrt.c
>
> diff --git a/configure b/configure
> index 82367fd30d..76cd56477a 100755
> --- a/configure
> +++ b/configure
> @@ -3476,6 +3476,7 @@ ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls"
>  ffrtmpcrypt_protocol_select="tcp_protocol"
>  ffrtmphttp_protocol_conflict="librtmp_protocol"
>  ffrtmphttp_protocol_select="http_protocol"
> +ffrtmpsrt_protocol_select="libsrt_protocol"
>  ftp_protocol_select="tcp_protocol"
>  gopher_protocol_select="tcp_protocol"
>  gophers_protocol_select="tls_protocol"
> @@ -3502,6 +3503,7 @@ rtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol"
>  rtmpte_protocol_suggest="zlib"
>  rtmpts_protocol_select="ffrtmphttp_protocol https_protocol"
>  rtmpts_protocol_suggest="zlib"
> +rtmpsrt_protocol_select="ffrtmpsrt_protocol"
>  rtp_protocol_select="udp_protocol"
>  schannel_conflict="openssl gnutls libtls mbedtls"
>  sctp_protocol_deps="struct_sctp_event_subscribe struct_msghdr_msg_flags"
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 85b5d8e7eb..7770fb2f8c 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -618,6 +618,7 @@ OBJS-$(CONFIG_CRYPTO_PROTOCOL)           += crypto.o
>  OBJS-$(CONFIG_DATA_PROTOCOL)             += data_uri.o
>  OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL)      += rtmpcrypt.o rtmpdigest.o rtmpdh.o
>  OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL)       += rtmphttp.o
> +OBJS-$(CONFIG_FFRTMPSRT_PROTOCOL)        += rtmpsrt.o
>  OBJS-$(CONFIG_FILE_PROTOCOL)             += file.o
>  OBJS-$(CONFIG_FTP_PROTOCOL)              += ftp.o urldecode.o
>  OBJS-$(CONFIG_GOPHER_PROTOCOL)           += gopher.o
> @@ -638,6 +639,7 @@ OBJS-$(CONFIG_RTMPS_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
>  OBJS-$(CONFIG_RTMPT_PROTOCOL)            += rtmpproto.o rtmpdigest.o rtmppkt.o
>  OBJS-$(CONFIG_RTMPTE_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
>  OBJS-$(CONFIG_RTMPTS_PROTOCOL)           += rtmpproto.o rtmpdigest.o rtmppkt.o
> +OBJS-$(CONFIG_RTMPSRT_PROTOCOL)          += rtmpproto.o rtmpdigest.o rtmppkt.o
>  OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o ip.o
>  OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
>  OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
> diff --git a/libavformat/protocols.c b/libavformat/protocols.c
> index 4b6b1c8e98..3f848338b0 100644
> --- a/libavformat/protocols.c
> +++ b/libavformat/protocols.c
> @@ -31,6 +31,7 @@ extern const URLProtocol ff_crypto_protocol;
>  extern const URLProtocol ff_data_protocol;
>  extern const URLProtocol ff_ffrtmpcrypt_protocol;
>  extern const URLProtocol ff_ffrtmphttp_protocol;
> +extern const URLProtocol ff_ffrtmpsrt_protocol;
>  extern const URLProtocol ff_file_protocol;
>  extern const URLProtocol ff_ftp_protocol;
>  extern const URLProtocol ff_gopher_protocol;
> @@ -51,6 +52,7 @@ extern const URLProtocol ff_rtmps_protocol;
>  extern const URLProtocol ff_rtmpt_protocol;
>  extern const URLProtocol ff_rtmpte_protocol;
>  extern const URLProtocol ff_rtmpts_protocol;
> +extern const URLProtocol ff_rtmpsrt_protocol;
>  extern const URLProtocol ff_rtp_protocol;
>  extern const URLProtocol ff_sctp_protocol;
>  extern const URLProtocol ff_srtp_protocol;
> diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
> index 5a540e3240..50e41662e8 100644
> --- a/libavformat/rtmpproto.c
> +++ b/libavformat/rtmpproto.c
> @@ -128,6 +128,7 @@ typedef struct RTMPContext {
>      char          auth_params[500];
>      int           do_reconnect;
>      int           auth_tried;
> +    int           rtmp_over_srt;
>  } RTMPContext;
>
>  #define PLAYER_KEY_OPEN_PART_LEN 30   ///< length of partial key used for first client digest signing
> @@ -2624,7 +2625,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o
>          }
>      }
>
> -    if (rt->listen && strcmp(proto, "rtmp")) {
> +    if (rt->listen && strcmp(proto, "rtmp") && strcmp(proto, "rtmpsrt")) {
>          av_log(s, AV_LOG_ERROR, "rtmp_listen not available for %s\n",
>                 proto);
>          return AVERROR(EINVAL);
> @@ -2647,6 +2648,12 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o
>          /* open the encrypted connection */
>          ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
>          rt->encrypted = 1;
> +    } else if (!strcmp(proto, "rtmpsrt") || rt->rtmp_over_srt) {
> +        if (rt->listen)
> +            av_dict_set(opts, "mode", "listener", 1);
> +        else
> +            av_dict_set(opts, "mode", "caller", 1);
> +        ff_url_join(buf, sizeof(buf), "ffrtmpsrt", NULL, hostname, port, "%s", path);
>      } else {
>          /* open the tcp connection */
>          if (port < 0)
> @@ -3116,6 +3123,7 @@ static const AVOption rtmp_options[] = {
>      {"rtmp_listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
>      {"listen",      "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
>      {"timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies -rtmp_listen 1",  OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
> +    {"rtmp_srt", "Force RTMP over SRT",  OFFSET(rtmp_over_srt), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC|ENC},
>      { NULL },
>  };
>
> @@ -3153,3 +3161,4 @@ RTMP_PROTOCOL(rtmps,  RTMPS)
>  RTMP_PROTOCOL(rtmpt,  RTMPT)
>  RTMP_PROTOCOL(rtmpte, RTMPTE)
>  RTMP_PROTOCOL(rtmpts, RTMPTS)
> +RTMP_PROTOCOL(rtmpsrt, RTMPSRT)
> diff --git a/libavformat/rtmpsrt.c b/libavformat/rtmpsrt.c
> new file mode 100644
> index 0000000000..0325973db9
> --- /dev/null
> +++ b/libavformat/rtmpsrt.c
> @@ -0,0 +1,167 @@
> +/*
> + * 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
> + */
> +
> +#include "libavutil/avstring.h"
> +#include "libavutil/intfloat.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/time.h"
> +#include "internal.h"
> +#include "url.h"
> +
> +typedef struct RTMP_SrtContext {
> +    const AVClass *class;
> +    URLContext   *stream;
> +    char buf[1500];
> +    int buf_len;
> +    int buf_index;
> +    char *streamid;
> +} RTMP_SrtContext;
> +
> +static int rtmp_srt_open(URLContext *h, const char *uri, int flags, AVDictionary **opts)
> +{
> +    RTMP_SrtContext *s = h->priv_data;
> +    char buf[512];
> +    char host[256];
> +    int port;
> +    char path[1024];
> +    char *streamid;
> +    char *p;
> +
> +    av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, path, sizeof(path), uri);
> +
> +    if (s->streamid) {
> +        streamid = av_strdup(s->streamid);
> +    } else {
> +        // rtmp path: /${app}/{stream}?txSecret=${txSecret}&txTime=${txTime}
> +        // streamid=#!::h=${rtmp-push-domain},r=${app}/${stream},txSecret=${txSecret},txTime=${txTime}
> +        for (p = path; *p; p++) {
> +            if (*p == '&' || *p == '?')
> +                *p = ',';
> +        }
> +        if (path[0] == '/')
> +            p = path + 1;
> +        else
> +            p = path;
> +        streamid = av_asprintf("#!::h=%s,r=%s", host, p);
> +    }
> +    av_log(h, AV_LOG_DEBUG, "streamid %s\n", streamid ? streamid : "");
> +    av_dict_set(opts, "streamid", streamid, AV_DICT_DONT_STRDUP_VAL);
> +
> +    av_dict_set(opts, "tlpktdrop", "0", 1);
> +    av_dict_set(opts, "payload_size", "max_size", 1);
> +
> +    ff_url_join(buf, sizeof(buf), "srt", NULL, host, port, NULL);
> +    return ffurl_open_whitelist(
> +        &s->stream, buf, AVIO_FLAG_READ_WRITE, &h->interrupt_callback, opts,
> +        h->protocol_whitelist, h->protocol_blacklist, h);
> +}
> +
> +static int read_from_buf(RTMP_SrtContext *s, unsigned char *buf, int size)
> +{
> +    int min = FFMIN(s->buf_len, size);
> +    memcpy(buf, s->buf + s->buf_index, min);
> +    if (min == s->buf_len) {
> +        s->buf_len = 0;
> +        s->buf_index = 0;
> +    } else {
> +        s->buf_len -= min;
> +        s->buf_index += min;
> +    }
> +    return min;
> +}
> +
> +static int rtmp_srt_read(URLContext *h, unsigned char *buf, int size)
> +{
> +    int ret;
> +    RTMP_SrtContext *s = h->priv_data;
> +    if (s->buf_len > 0) {
> +        return read_from_buf(s, buf, size);
> +    }
> +
> +    if (h->flags & AVIO_FLAG_NONBLOCK)
> +        s->stream->flags |= AVIO_FLAG_NONBLOCK;
> +    else
> +        s->stream->flags &= ~AVIO_FLAG_NONBLOCK;
> +    ret = ffurl_read(s->stream, s->buf, s->stream->max_packet_size);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +    s->buf_len = ret;
> +    s->buf_index = 0;
> +    return read_from_buf(s, buf, size);
> +}
> +
> +static int rtmp_srt_write(URLContext *h, const unsigned char *buf, int size)
> +{
> +    int ret;
> +    int n;
> +    int len = 0;
> +    RTMP_SrtContext *s = h->priv_data;
> +
> +    if (h->flags & AVIO_FLAG_NONBLOCK)
> +        s->stream->flags |= AVIO_FLAG_NONBLOCK;
> +    else
> +        s->stream->flags &= ~AVIO_FLAG_NONBLOCK;
> +    while (size > 0) {
> +        n = size > s->stream->max_packet_size ? s->stream->max_packet_size : size;
> +        ret = ffurl_write(s->stream, buf + len, n);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +        len += ret;
> +        size -= ret;
> +    }
> +
> +    return len;
> +}
> +
> +static int rtmp_srt_close(URLContext *h)
> +{
> +    RTMP_SrtContext *s = h->priv_data;
> +    return ffurl_closep(&s->stream);
> +}
> +
> +#define OFFSET(x) offsetof(RTMP_SrtContext, x)
> +#define DEC AV_OPT_FLAG_DECODING_PARAM
> +#define ENC AV_OPT_FLAG_ENCODING_PARAM
> +
> +static const AVOption ffrtmpsrt_options[] = {
> +    // There is a streamid option in ffmpeg_opt. When libsrt is used by rtmp,
> +    // the streamid option was passed to ffmpeg_opt and leads to error.
> +    { "rtmpsrt_streamid", "A string of up to 512 characters that an Initiator can pass to a Responder", OFFSET(streamid), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = DEC|ENC },
> +    { NULL },
> +};
> +
> +static const AVClass ffrtmpsrt_class = {
> +    .class_name = "ffrtmpsrt",
> +    .item_name  = av_default_item_name,
> +    .option     = ffrtmpsrt_options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +const URLProtocol ff_ffrtmpsrt_protocol = {
> +    .name           = "ffrtmpsrt",
> +    .url_open2      = rtmp_srt_open,
> +    .url_read       = rtmp_srt_read,
> +    .url_write      = rtmp_srt_write,
> +    .url_close      = rtmp_srt_close,
> +    .priv_data_size = sizeof(RTMP_SrtContext),
> +    .flags          = URL_PROTOCOL_FLAG_NETWORK,
> +    .priv_data_class= &ffrtmpsrt_class,
> +    .default_whitelist = "srt",
> +};


More information about the ffmpeg-devel mailing list