[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