[FFmpeg-devel] [PATCH] Add RTMFP support with librtmfp library
Thomas Jammet
jammetthomas at gmail.com
Fri Apr 19 20:20:50 EEST 2019
Here is the updated version of the patch.
Thomas Jammet
---
configure | 4 +
doc/protocols.texi | 120 +++++++++++++++++++
libavformat/Makefile | 1 +
libavformat/librtmfp.c | 247 ++++++++++++++++++++++++++++++++++++++++
libavformat/protocols.c | 1 +
5 files changed, 373 insertions(+)
create mode 100644 libavformat/librtmfp.c
diff --git a/configure b/configure
index e10e2c2c46..1ba9c08621 100755
--- a/configure
+++ b/configure
@@ -257,6 +257,7 @@ External library support:
--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]
+ --enable-librtmfp enable RTMFP support via librtmfp [no]
--enable-libshine enable fixed-point MP3 encoding via libshine [no]
--enable-libsmbclient enable Samba protocol via libsmbclient [no]
--enable-libsnappy enable Snappy compression, needed for hap
encoding [no]
@@ -1775,6 +1776,7 @@ EXTERNAL_LIBRARY_LIST="
libpulse
librsvg
librtmp
+ librtmfp
libshine
libsmbclient
libsnappy
@@ -3386,6 +3388,7 @@ librtmpe_protocol_deps="librtmp"
librtmps_protocol_deps="librtmp"
librtmpt_protocol_deps="librtmp"
librtmpte_protocol_deps="librtmp"
+librtmfp_protocol_deps="librtmfp"
libsmbclient_protocol_deps="libsmbclient gplv3"
libsrt_protocol_deps="libsrt"
libsrt_protocol_select="network"
@@ -6189,6 +6192,7 @@ enabled libopus && {
enabled libpulse && require_pkg_config libpulse libpulse
pulse/pulseaudio.h pa_context_new
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 librtmfp && require_pkg_config librtmfp librtmfp
librtmfp/librtmfp.h RTMFP_Connect
enabled librubberband && require_pkg_config librubberband
"rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new
-lstdc++ && append librubberband_extralibs "-lstdc++"
enabled libshine && require_pkg_config libshine shine
shine/layer3.h shine_encode_buffer
enabled libsmbclient && { check_pkg_config libsmbclient
smbclient libsmbclient.h smbc_init ||
diff --git a/doc/protocols.texi b/doc/protocols.texi
index 3e4e7af3d4..61852d4d48 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -842,6 +842,126 @@ To play the same stream using @command{ffplay}:
ffplay "rtmp://myserver/live/mystream live=1"
@end example
+ at section librtmfp
+
+Real-Time Media Flow Protocol.
+
+Requires the presence of the librtmfp headers and library during
+configuration. You need to explicitly configure the build with
+"--enable-librtmfp".
+
+This protocol provides most functionalities of the UDP protocol RTMFP :
+unicast, direct p2p and multicast (via NetGroup).
+
+The required syntax for an RTMFP URL is:
+ at example
+rtmfp://@var{hostname}[:@var{port}][/@var{app}][/@var{playpath}]
+ at end example
+
+The accepted parameters are:
+ at table @option
+
+ at item hostname
+The address of the RTMFP server.
+
+ at item port
+The number of the UDP port to use (by default is 1935).
+
+ at item app
+It is the name of the application to access. It usually corresponds to the
+path where the application is installed on the RTMFP server (e.g.
+/ondemand/,/flash/live/, etc.).
+
+ at item playpath
+It is the path or name of the resource to play with reference to the
+application specified in app.
+ at end table
+
+Additionally, the following parameters can be set via command line options
+(or in code via @code{AVOption}s):
+ at table @option
+
+ at item rtmfp_socketReceiveSize
+Socket receive buffer size.
+
+ at item rtmfp_socketSendSize
+Socket send buffer size.
+
+ at item rtmfp_audioUnbuffered
+Unbuffered audio mode (default to false).
+
+ at item rtmfp_videoUnbuffered
+Unbuffered video mode (default to false).
+
+ at item rtmfp_peerId
+Connect to a peer for playing.
+
+ at item rtmfp_p2pPublishing
+Publish the stream in p2p mode (default to false).
+
+ at item rtmfp_netgroup
+Publish/Play the stream into a NetGroup (multicast).
+
+ at item rtmfp_fallbackUrl
+(Only with @code{rtmfp_negtroup}) Try to play an RTMFP unicast stream url
+until the NetGroup connection is not ready. Can produce undefined behavior
+if the stream codecs are different.
+
+ at item rtmfp_fallbackTimeout
+(Only with @code{rtmfp_negtroup}) Set the timeout in milliseconds to start
+fallback to unicast.
+
+ at item rtmfp_disableRateControl
+(Only with @code{rtmfp_negtroup}) Disable the P2P connection rate control
+to avoid unwanted disconnection.
+
+ at item rtmfp_pushLimit
+(Only with @code{rtmfp_negtroup}) Specifies the maximum number (-1) of
+peers to which we will send push fragments.
+
+ at item rtmfp_updatePeriod
+(Only with @code{rtmfp_negtroup}) Specifies the interval in milliseconds
+between messages sent to peers informating them that the local node has
+new p2p multicast media fragments available.
+
+ at item rtmfp_windowDuration
+(Only with @code{rtmfp_negtroup}) Specifies the duration in milliseconds
+of the p2p multicast reassembly window.
+
+ at item rtmfp_swfurl
+URL of the SWF player. By default no value will be sent.
+
+ at item rtmfp_app
+Name of application to connect to on the RTMFP server (by default 'live').
+
+ at item rtmfp_pageurl
+URL of the web page in which the media was embedded. By default no value
+will be sent.
+
+ at item rtmfp_flashver
+Version of the Flash plugin used to run the SWF player. By default
+ at code{WIN 20,0,0,286}.
+
+ at item rtmfp_host
+IPv4 host address to bind to (use this if you ave multiple interfaces).
+
+ at item rtmfp_hostIPv6
+IPv6 host address to bind to (use this if you ave multiple interfaces).
+ at end table
+
+For example to read with @command{ffplay} a multimedia resource named
+"sample" from the application "vod" from an RTMFP server "myserver":
+ at example
+ffplay rtmfp://myserver/vod/sample
+ at end example
+
+To publish a multimedia resource named "sample" to an RTMFP server:
+ at example
+ffmpeg -re -i <input> -f flv rtmfp://myserver/sample
+ at end example
+
+For more information see: @url{https://github.com/MonaSolutions/librtmfp}.
+
@section rtp
Real-time Transport Protocol.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 99be60d184..be1a7b15c0 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -627,6 +627,7 @@ OBJS-$(CONFIG_LIBRTMPE_PROTOCOL) += librtmp.o
OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o
OBJS-$(CONFIG_LIBRTMPT_PROTOCOL) += librtmp.o
OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o
+OBJS-$(CONFIG_LIBRTMFP_PROTOCOL) += librtmfp.o
OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o
OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o
OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o
diff --git a/libavformat/librtmfp.c b/libavformat/librtmfp.c
new file mode 100644
index 0000000000..a9a72f36e2
--- /dev/null
+++ b/libavformat/librtmfp.c
@@ -0,0 +1,247 @@
+/*
+ * RTMFP network protocol
+ * Copyright (c) 2019 Thomas Jammet
+ *
+ * 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
+ * RTMFP protocol based on https://github.com/MonaSolutions/librtmfp librtmfp
+ */
+
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#if CONFIG_NETWORK
+#include "network.h"
+#endif
+#include <sys/time.h>
+
+#include <librtmfp/librtmfp.h>
+
+typedef struct LibRTMFPContext {
+ const AVClass* class;
+ RTMFPConfig rtmfp;
+ unsigned int id;
+ int audioUnbuffered;
+ int videoUnbuffered;
+ int p2pPublishing;
+ char* peerId;
+ char* publication;
+ unsigned short streamId;
+ const char* swfUrl;
+ const char* app;
+ const char* pageUrl;
+ const char* flashVer;
+ const char* host;
+ const char* hostIPv6;
+
+ // General options
+ int socketReceiveSize;
+ int socketSendSize;
+
+ // NetGroup members
+ RTMFPGroupConfig group;
+ char* netgroup;
+ unsigned int updatePeriod;
+ unsigned int windowDuration;
+ unsigned int pushLimit;
+ char* fallbackUrl;
+ unsigned int fallbackTimeout;
+ int disableRateCtl;
+} LibRTMFPContext;
+
+static void rtmfp_log(unsigned int level, const char* fileName, long
line, const char* message)
+{
+ const char* strLevel = "";
+
+ switch (level) {
+ default:
+ case 1: level = AV_LOG_FATAL; strLevel = "FATAL"; break;
+ case 2:
+ case 3: level = AV_LOG_ERROR; strLevel = "ERROR"; break;
+ case 4: level = AV_LOG_WARNING; strLevel = "WARN"; break;
+ case 5:
+ case 6: level = AV_LOG_INFO; strLevel = "INFO"; break;
+ case 7: level = AV_LOG_DEBUG; strLevel = "DEBUG"; break;
+ case 8: level = AV_LOG_TRACE; strLevel = "TRACE"; break;
+ }
+
+ av_log(NULL, level, "[%s] %s\n", strLevel, message);
+}
+
+static int rtmfp_close(URLContext *s)
+{
+ av_log(s, AV_LOG_INFO, "Closing RTMFP connection...\n");
+ RTMFP_Terminate();
+ return 0;
+}
+
+static void onStatusEvent(const char* code, const char* description) {
+ av_log(NULL, AV_LOG_INFO, "onStatusEvent : %s - %s\n", code, description);
+}
+
+/**
+ * Open RTMFP connection and verify that the stream can be played.
+ *
+ * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]...
+ * where 'app' is first one or two directories in the path
+ * (e.g. /ondemand/, /flash/live/, etc.)
+ * and 'playpath' is a file name (the rest of the path,
+ * may be prefixed with "mp4:")
+ *
+ * Additional RTMFP library options may be appended as
+ * space-separated key-value pairs.
+ */
+static int rtmfp_open(URLContext *s, const char *uri, int flags)
+{
+ LibRTMFPContext *ctx = s->priv_data;
+ int level = 0;
+
+ switch (av_log_get_level()) {
+ case AV_LOG_FATAL: level = 1; break;
+ case AV_LOG_ERROR: level = 3; break;
+ case AV_LOG_WARNING: level = 4; break;
+ default:
+ case AV_LOG_INFO: level = 6; break;
+ case AV_LOG_DEBUG: level = 7; break;
+ case AV_LOG_VERBOSE: level = 8; break;
+ case AV_LOG_TRACE: level = 8; break;
+ }
+
+ RTMFP_SetIntParameter("socketReceiveSize", ctx->socketReceiveSize);
+ RTMFP_SetIntParameter("socketSendSize", ctx->socketSendSize);
+ RTMFP_SetIntParameter("timeoutFallback", ctx->fallbackTimeout);
+ RTMFP_SetIntParameter("logLevel", level);
+
+ RTMFP_Init(&ctx->rtmfp, &ctx->group, 1);
+ ctx->rtmfp.pOnStatusEvent = onStatusEvent;
+ ctx->rtmfp.isBlocking = 1;
+ ctx->rtmfp.swfUrl = ctx->swfUrl;
+ ctx->rtmfp.app = ctx->app;
+ ctx->rtmfp.pageUrl = ctx->pageUrl;
+ ctx->rtmfp.flashVer = ctx->flashVer;
+ ctx->rtmfp.host = ctx->host;
+ ctx->rtmfp.hostIPv6 = ctx->hostIPv6;
+
+ RTMFP_LogSetCallback(rtmfp_log);
+ /*RTMFP_ActiveDump();
+ RTMFP_DumpSetCallback(rtmfp_dump);*/
+ RTMFP_InterruptSetCallback(s->interrupt_callback.callback,
s->interrupt_callback.opaque);
+
+ RTMFP_GetPublicationAndUrlFromUri(uri, &ctx->publication);
+
+ if ((ctx->id = RTMFP_Connect(uri, &ctx->rtmfp)) == 0)
+ return -1;
+
+ av_log(s, AV_LOG_INFO, "RTMFP Connect called : %d\n", ctx->id);
+
+ // Wait for connection to happen
+ if (RTMFP_WaitForEvent(ctx->id, RTMFP_CONNECTED) == 0)
+ return -1;
+
+ if (ctx->netgroup) {
+ ctx->group.netGroup = ctx->netgroup;
+ ctx->group.availabilityUpdatePeriod = ctx->updatePeriod;
+ ctx->group.windowDuration = ctx->windowDuration;
+ ctx->group.pushLimit = ctx->pushLimit;
+ ctx->group.isPublisher = (flags & AVIO_FLAG_WRITE) > 1;
+ ctx->group.isBlocking = 1;
+ ctx->group.disableRateControl = ctx->disableRateCtl>0;
+ ctx->streamId = RTMFP_Connect2Group(ctx->id,
ctx->publication, &ctx->rtmfp, &ctx->group, !ctx->audioUnbuffered,
!ctx->videoUnbuffered, ctx->fallbackUrl);
+ } else if (ctx->peerId)
+ ctx->streamId = RTMFP_Connect2Peer(ctx->id, ctx->peerId,
ctx->publication, 1);
+ else if (ctx->p2pPublishing)
+ ctx->streamId = RTMFP_PublishP2P(ctx->id, ctx->publication,
!ctx->audioUnbuffered, !ctx->videoUnbuffered, 1);
+ else if (flags & AVIO_FLAG_WRITE)
+ ctx->streamId = RTMFP_Publish(ctx->id, ctx->publication,
!ctx->audioUnbuffered, !ctx->videoUnbuffered, 1);
+ else
+ ctx->streamId = RTMFP_Play(ctx->id, ctx->publication);
+
+ if (!ctx->streamId)
+ return -1;
+
+ s->is_streamed = 1;
+ return 0;
+}
+
+static int rtmfp_write(URLContext *s, const uint8_t *buf, int size)
+{
+ LibRTMFPContext *ctx = s->priv_data;
+ int res = 0;
+
+ res = RTMFP_Write(ctx->id, buf, size);
+ return (res < 0)? AVERROR_UNKNOWN : res;
+}
+
+static int rtmfp_read(URLContext *s, uint8_t *buf, int size)
+{
+ LibRTMFPContext *ctx = s->priv_data;
+ int res = 0;
+
+ res = RTMFP_Read(ctx->streamId, ctx->id, buf, size);
+
+ return (res < 0)? AVERROR_UNKNOWN : res;
+}
+
+#define OFFSET(x) offsetof(LibRTMFPContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ {"socketReceiveSize", "Socket receive buffer size",
OFFSET(socketReceiveSize), AV_OPT_TYPE_INT, {.i64 = 212992}, 0,
0x0FFFFFFF, DEC|ENC},
+ {"socketSendSize", "Socket send buffer size",
OFFSET(socketSendSize), AV_OPT_TYPE_INT, {.i64 = 212992}, 0,
0x0FFFFFFF, DEC|ENC},
+ {"audioUnbuffered", "Unbuffered audio mode (default to false)",
OFFSET(audioUnbuffered), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1,
DEC|ENC},
+ {"videoUnbuffered", "Unbuffered video mode (default to false)",
OFFSET(videoUnbuffered), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1,
DEC|ENC},
+ {"peerId", "Connect to a peer for playing", OFFSET(peerId),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
+ {"p2pPublishing", "Publish the stream in p2p mode (default to
false)", OFFSET(p2pPublishing), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1,
DEC|ENC},
+ {"netgroup", "Publish/Play the stream into a NetGroup
(multicast)", OFFSET(netgroup), AV_OPT_TYPE_STRING, {.str = NULL }, 0,
0, DEC|ENC},
+ {"fallbackUrl", "Try to play a unicast stream url until the
NetGroup connection is not ready (can produce undefined behavior if
the stream codecs are different)",
+ OFFSET(fallbackUrl), AV_OPT_TYPE_STRING, {.str = NULL }, 0,
0, DEC|ENC},
+ {"fallbackTimeout", "Set the timeout in milliseconds to start
fallback to unicast", OFFSET(fallbackTimeout), AV_OPT_TYPE_INT, {.i64
= 8000 }, 0, 120000, DEC|ENC},
+ {"disableRateControl", "For Netgroup disable the P2P connection
rate control to avoid disconnection", OFFSET(disableRateCtl),
AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, DEC|ENC},
+ {"pushLimit", "Specifies the maximum number (-1) of peers to
which we will send push fragments", OFFSET(pushLimit),
AV_OPT_TYPE_INT, {.i64 = 4 }, 0, 255, DEC|ENC},
+ {"updatePeriod", "Specifies the interval in milliseconds between
messages sent to peers informating them that the local node has new
p2p multicast media fragments available",
+ OFFSET(updatePeriod), AV_OPT_TYPE_INT, {.i64 = 100 }, 100,
10000, DEC|ENC},
+ {"windowDuration", "Specifies the duration in milliseconds of the
p2p multicast reassembly window", OFFSET(windowDuration),
AV_OPT_TYPE_INT, {.i64 = 8000 }, 1000, 60000, DEC|ENC},
+ {"rtmfp_swfurl", "URL of the SWF player. By default no value will
be sent", OFFSET(swfUrl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0,
DEC|ENC},
+ {"rtmfp_app", "Name of application to connect to on the RTMFP
server (by default 'live')", OFFSET(app), AV_OPT_TYPE_STRING, {.str =
NULL }, 0, 0, DEC|ENC},
+ {"rtmfp_pageurl", "URL of the web page in which the media was
embedded. By default no value will be sent.", OFFSET(pageUrl),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
+ {"rtmfp_flashver", "Version of the Flash plugin used to run the
SWF player. By default 'WIN 20,0,0,286'", OFFSET(flashVer),
AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
+ {"rtmfp_host", "IPv4 host address to bind to (use this if you ave
multiple interfaces)", OFFSET(host), AV_OPT_TYPE_STRING, {.str = NULL
}, 0, 0, DEC|ENC},
+ {"rtmfp_hostIPv6", "IPv6 host address to bind to (use this if you
ave multiple interfaces)", OFFSET(hostIPv6), AV_OPT_TYPE_STRING, {.str
= NULL }, 0, 0, DEC|ENC},
+ { NULL },
+};
+
+static const AVClass librtmfp_class = {
+ .class_name = "librtmfp protocol",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_librtmfp_protocol = {
+ .name = "rtmfp",
+ .url_open = rtmfp_open,
+ .url_read = rtmfp_read,
+ .url_write = rtmfp_write,
+ .url_close = rtmfp_close,
+ .priv_data_size = sizeof(LibRTMFPContext),
+ .priv_data_class = &librtmfp_class,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+};
+
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index ad95659795..7ac985b1bc 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -65,6 +65,7 @@ extern const URLProtocol ff_librtmpe_protocol;
extern const URLProtocol ff_librtmps_protocol;
extern const URLProtocol ff_librtmpt_protocol;
extern const URLProtocol ff_librtmpte_protocol;
+extern const URLProtocol ff_librtmfp_protocol;
extern const URLProtocol ff_libsrt_protocol;
extern const URLProtocol ff_libssh_protocol;
extern const URLProtocol ff_libsmbclient_protocol;
--
2.19.1
More information about the ffmpeg-devel
mailing list