[FFmpeg-devel] Add RTMFP support with librtmfp library

Submitted by Thomas Jammet on April 19, 2019, 5:20 p.m.

Details

Message ID CAFDuMW4Xr5kzm_hzd7nsfhB6WCqzTsChGWc-hi6xh1JRJiqn_A@mail.gmail.com
State New
Headers show

Commit Message

Thomas Jammet April 19, 2019, 5:20 p.m.
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

--
2.19.1

Comments

Moritz Barsnick April 19, 2019, 7:31 p.m.
On Fri, Apr 19, 2019 at 19:20:50 +0200, Thomas Jammet wrote:
> @@ -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

It looks like your mail client automatically wrapped the lines of the
patch, which will not allow it to be applied. You need to reconfigure
your mailer, or attach the patch (or use git send-email).

> +@item rtmfp_socketReceiveSize

CamelCase options aren't accepted by the project, AFAIU.

> +(Only with @code{rtmfp_negtroup}) Try to play an RTMFP unicast stream url
                    ^
You nicely misspelled this option reference six times (thanks to
copy/paste, likely). ;-)

> +(Only with @code{rtmfp_negtroup}) Specifies the maximum number (-1) of
> +peers to which we will send push fragments.

Does -1 mean no maximum? Or what does it mean here?

>  OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL)        += librtmp.o
> +OBJS-$(CONFIG_LIBRTMFP_PROTOCOL)         += librtmfp.o
>  OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL)     += libsmbclient.o

This is not quite the correct alphabetical order.

> +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;

The asterisks attach to the variable, not the type.

And ffmpeg doesn't use CamelCase for regular variables.

> +static void onStatusEvent(const char* code, const char* description) {
> +  av_log(NULL, AV_LOG_INFO, "onStatusEvent : %s - %s\n", code, description);
> +}

This should get a log context.

> +    int res = 0;
> +
> +    res = RTMFP_Write(ctx->id, buf, size);

The first assignment is redundant.

> +    int res = 0;
> +
> +    res = RTMFP_Read(ctx->streamId, ctx->id, buf, size);

Same here.

> +    return (res < 0)? AVERROR_UNKNOWN : res;

Can you not find a better error code?

> +    {"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},

These defaults usually don't need to be documented here (ffmpeg -h does
that for you).

> +    {"netgroup", "Publish/Play the stream into a NetGroup
> (multicast)", OFFSET(netgroup), AV_OPT_TYPE_STRING, {.str = NULL }, 0,
> 0, DEC|ENC},

The description makes it sound like a boolean. What does this parameter
actually expect?

> +    {"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},

I still don't understand the "-1". And try to avoid the "we". (Yes, I
know passive form is sometimes considered weird in English.)

> +    {"updatePeriod", "Specifies the interval in milliseconds between
> messages sent to peers informating them that the local node has new
> p2p multicast media fragments available",

I believe the description should be shorter. (You can additionally save
some by dropping the "specifies", as each parameter specifies something
by nature.)

This was likely not complete, and no technical review though, sorry.

Cheers,
Moritz

Patch hide | download patch | download mbox

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

+@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:
+@example
+rtmfp://@var{hostname}[:@var{port}][/@var{app}][/@var{playpath}]
+@end example
+
+The accepted parameters are:
+@table @option
+
+@item hostname
+The address of the RTMFP server.
+
+@item port
+The number of the UDP port to use (by default is 1935).
+
+@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.).
+
+@item playpath
+It is the path or name of the resource to play with reference to the
+application specified in app.
+@end table
+
+Additionally, the following parameters can be set via command line options
+(or in code via @code{AVOption}s):
+@table @option
+
+@item rtmfp_socketReceiveSize
+Socket receive buffer size.
+
+@item rtmfp_socketSendSize
+Socket send buffer size.
+
+@item rtmfp_audioUnbuffered
+Unbuffered audio mode (default to false).
+
+@item rtmfp_videoUnbuffered
+Unbuffered video mode (default to false).
+
+@item rtmfp_peerId
+Connect to a peer for playing.
+
+@item rtmfp_p2pPublishing
+Publish the stream in p2p mode (default to false).
+
+@item rtmfp_netgroup
+Publish/Play the stream into a NetGroup (multicast).
+
+@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.
+
+@item rtmfp_fallbackTimeout
+(Only with @code{rtmfp_negtroup}) Set the timeout in milliseconds to start
+fallback to unicast.
+
+@item rtmfp_disableRateControl
+(Only with @code{rtmfp_negtroup}) Disable the P2P connection rate control
+to avoid unwanted disconnection.
+
+@item rtmfp_pushLimit
+(Only with @code{rtmfp_negtroup}) Specifies the maximum number (-1) of
+peers to which we will send push fragments.
+
+@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.
+
+@item rtmfp_windowDuration
+(Only with @code{rtmfp_negtroup}) Specifies the duration in milliseconds
+of the p2p multicast reassembly window.
+
+@item rtmfp_swfurl
+URL of the SWF player. By default no value will be sent.
+
+@item rtmfp_app
+Name of application to connect to on the RTMFP server (by default 'live').
+
+@item rtmfp_pageurl
+URL of the web page in which the media was embedded. By default no value
+will be sent.
+
+@item rtmfp_flashver
+Version of the Flash plugin used to run the SWF player. By default
+@code{WIN 20,0,0,286}.
+
+@item rtmfp_host
+IPv4 host address to bind to (use this if you ave multiple interfaces).
+
+@item rtmfp_hostIPv6
+IPv6 host address to bind to (use this if you ave multiple interfaces).
+@end table
+
+For example to read with @command{ffplay} a multimedia resource named
+"sample" from the application "vod" from an RTMFP server "myserver":
+@example
+ffplay rtmfp://myserver/vod/sample
+@end example
+
+To publish a multimedia resource named "sample" to an RTMFP server:
+@example
+ffmpeg -re -i <input> -f flv rtmfp://myserver/sample
+@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;