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", > +}; _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".