This adds two protocols, but one of them is an internal implementation
detail just used as an abstraction layer/generalization in the code. The
RTMPT protocol implementation uses rtmphttp:// as an alternative to the
tcp:// protocol. This allows moving most of the lower level logic out
from the higher level generic rtmp code.
---
- Fix missing proper dependecies in configure
- Fix capacity of the output buffer in rtmp_http_write()
- Fix a case of ==
- Return EIO when off == sizeof(client_id)
 Changelog                |    1 +
 configure                |    4 +
 doc/general.texi         |    1 +
 doc/protocols.texi       |    8 ++
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    2 +
 libavformat/rtmphttp.c   |  220 ++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/rtmpproto.c  |   31 ++++++-
 libavformat/version.h    |    4 +-
 9 files changed, 267 insertions(+), 5 deletions(-)
 create mode 100644 libavformat/rtmphttp.c

diff --git a/Changelog b/Changelog
index b80ff88..4288aa3 100644
--- a/Changelog
+++ b/Changelog
@@ -25,6 +25,7 @@ version <next>:
   be used with -of old.
 - Indeo Audio decoder
 - channelsplit audio filter
+- RTMPT protocol support
 
 
 version 0.8:
diff --git a/configure b/configure
index 4bb2030..d614366 100755
--- a/configure
+++ b/configure
@@ -1511,6 +1511,10 @@ mmsh_protocol_select="http_protocol"
 mmst_protocol_deps="network"
 rtmp_protocol_deps="!librtmp_protocol"
 rtmp_protocol_select="tcp_protocol"
+rtmphttp_protocol_deps="!librtmp_protocol"
+rtmphttp_protocol_select="http_protocol"
+rtmpt_protocol_deps="!librtmp_protocol"
+rtmpt_protocol_select="rtmphttp_protocol"
 rtp_protocol_select="udp_protocol"
 sctp_protocol_deps="network netinet_sctp_h"
 tcp_protocol_deps="network"
diff --git a/doc/general.texi b/doc/general.texi
index 354067d..1c81774 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -811,6 +811,7 @@ performance on systems without hardware floating point 
support).
 @item MMS          @tab X
 @item pipe         @tab X
 @item RTMP         @tab X
+@item RTMPT        @tab X
 @item RTP          @tab X
 @item TCP          @tab X
 @item UDP          @tab X
diff --git a/doc/protocols.texi b/doc/protocols.texi
index 8492033..0b4f1b1 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -243,6 +243,14 @@ For example to read with @command{avplay} a multimedia 
resource named
 avplay rtmp://myserver/vod/sample
 @end example
 
+@section rtmpt
+
+Real-Time Messaging Protocol tunneled through HTTP.
+
+The Real-Time Messaging Protocol tunneled through HTTP (RTMPT) is used
+for streaming multimedia content within HTTP requests to traverse
+firewalls.
+
 @section rtmp, rtmpe, rtmps, rtmpt, rtmpte
 
 Real-Time Messaging Protocol and its variants supported through
diff --git a/libavformat/Makefile b/libavformat/Makefile
index ca4f7a0..b8d93cd 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -345,6 +345,7 @@ OBJS-$(CONFIG_MMST_PROTOCOL)             += mmst.o mms.o 
asf.o
 OBJS-$(CONFIG_MD5_PROTOCOL)              += md5proto.o
 OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
 OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmppkt.o
+OBJS-$(CONFIG_RTMPHTTP_PROTOCOL)         += rtmphttp.o rtmpproto.o rtmppkt.o
 OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o
 OBJS-$(CONFIG_SCTP_PROTOCOL)             += sctp.o
 OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 1320a28..69f27ab 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -256,6 +256,8 @@ void av_register_all(void)
     REGISTER_PROTOCOL (MD5,  md5);
     REGISTER_PROTOCOL (PIPE, pipe);
     REGISTER_PROTOCOL (RTMP, rtmp);
+    REGISTER_PROTOCOL (RTMPT, rtmpt);
+    REGISTER_PROTOCOL (RTMPHTTP, rtmphttp);
     REGISTER_PROTOCOL (RTP, rtp);
     REGISTER_PROTOCOL (SCTP, sctp);
     REGISTER_PROTOCOL (TCP, tcp);
diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c
new file mode 100644
index 0000000..d9bf028
--- /dev/null
+++ b/libavformat/rtmphttp.c
@@ -0,0 +1,220 @@
+/*
+ * RTMP HTTP network protocol
+ * Copyright (c) 2012 Samuel Pitoiset
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * RTMP HTTP protocol
+ */
+
+#include "libavutil/avstring.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "http.h"
+
+#define RTMPT_DEFAULT_PORT 80
+
+/* protocol handler state */
+typedef enum {
+    STATE_START,        ///< client has not done anything yet
+    STATE_OPENED,       ///< client has opened the connection
+    STATE_WAITING,      ///< client is waiting for a server reply
+    STATE_READY,        ///< client is ready for sending requests
+} ClientState;
+
+/* protocol handler context */
+typedef struct RTMP_HTTPContext {
+    URLContext   *stream;           ///< HTTP stream
+    char         host[256];         ///< hostname of the server
+    int          port;              ///< port to connect (default is 80)
+    char         client_id[64];     ///< client ID used for all requests 
except the first one
+    int          seq;               ///< sequence ID used for all requests
+    ClientState  state;             ///< current state
+    uint8_t      *out_data;         ///< output buffer
+    int          out_size;          ///< current output buffer size
+    int          out_capacity;      ///< current output buffer capacity
+} RTMP_HTTPContext;
+
+static int rtmp_http_send_cmd(URLContext *h, const char *cmd)
+{
+    RTMP_HTTPContext *rt = h->priv_data;
+    char uri[2048];
+    uint8_t c;
+    int ret;
+
+    ff_url_join(uri, sizeof(uri), "http", NULL, rt->host, rt->port,
+                "/%s/%s/%d", cmd, rt->client_id, rt->seq++);
+
+    av_opt_set_bin(rt->stream->priv_data, "post_data", rt->out_data,
+                   rt->out_size, 0);
+
+    /* send a new request to the server */
+    if ((ret = ff_http_do_new_request(rt->stream, uri)) < 0)
+        return ret;
+
+    /* re-init output buffer */
+    rt->out_size = 0;
+
+    /* read the first byte which contains the polling interval */
+    if ((ret = ffurl_read(rt->stream, &c, 1)) < 0)
+        return ret;
+
+    return ret;
+}
+
+static int rtmp_http_write(URLContext *h, const uint8_t *buf, int size)
+{
+    RTMP_HTTPContext *rt = h->priv_data;
+    void *ptr;
+
+    if (rt->out_size + size > rt->out_capacity) {
+        rt->out_capacity += (rt->out_size + size) * 2;
+        ptr = av_realloc(rt->out_data, rt->out_capacity);
+        if (!ptr)
+            return AVERROR(ENOMEM);
+        rt->out_data = ptr;
+    }
+
+    memcpy(rt->out_data + rt->out_size, buf, size);
+    rt->out_size += size;
+
+    return size;
+}
+
+static int rtmp_http_read(URLContext *h, uint8_t *buf, int size)
+{
+    RTMP_HTTPContext *rt = h->priv_data;
+    int ret, off = 0;
+
+    /* try to read at least 1 byte of data */
+    do {
+        ret = ffurl_read(rt->stream, buf + off, size);
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+
+        if (ret == AVERROR_EOF) {
+            /* When the client has reached end of file for the last request,
+             * we have to send a new request if we have buffered data.
+             * Otherwise, we have to send an idle POST. */
+            if (rt->out_size > 0) {
+                if ((ret = rtmp_http_send_cmd(h, "send")) < 0)
+                    return ret;
+            } else {
+                if ((ret = rtmp_http_write(h, "", 1)) < 0)
+                    return ret;
+
+                if ((ret = rtmp_http_send_cmd(h, "idle")) < 0)
+                    return ret;
+            }
+        } else {
+            off  += ret;
+            size -= ret;
+        }
+    } while (off <= 0);
+
+    return off;
+}
+
+static int rtmp_http_close(URLContext *h)
+{
+    RTMP_HTTPContext *rt = h->priv_data;
+    int ret = 0;
+
+    if (rt->state > STATE_OPENED) {
+        // TODO: Add support for the nonblocking variant.
+        if ((ret = rtmp_http_write(h, "", 1)) > 0)
+            ret = rtmp_http_send_cmd(h, "close");
+    }
+
+    av_freep(&rt->out_data);
+    ffurl_close(rt->stream);
+
+    return ret;
+}
+
+static int rtmp_http_open(URLContext *h, const char *uri, int flags)
+{
+    RTMP_HTTPContext *rt = h->priv_data;
+    char headers[1024], url[1024];
+    int ret, off = 0;
+
+    av_url_split(NULL, 0, NULL, 0, rt->host, sizeof(rt->host), &rt->port,
+                 NULL, 0, uri);
+
+    rt->state = STATE_START;
+    if (rt->port < 0)
+        rt->port = RTMPT_DEFAULT_PORT;
+
+    /* This is the first request that is sent to the server in order to
+     * register a client on the server and start a new session. The server
+     * replies with a unique id (usually a number) that is used by the client
+     * for all future requests.
+     * Note: the reply doesn't contain a value for the polling interval.
+     * A successful connect resets the consecutive index that is used
+     * in the URLs. */
+    ff_url_join(url, sizeof(url), "http", NULL, rt->host, rt->port, "/open/1");
+
+    /* alloc the http context */
+    if ((ret = ffurl_alloc(&rt->stream, url, AVIO_FLAG_READ_WRITE, NULL)) < 0)
+        return ret;
+
+    /* set options */
+    snprintf(headers, sizeof(headers),
+             "Cache-Control: no-cache\r\n"
+             "Content-type: application/x-fcs\r\n"
+             "User-Agent: Shockwave Flash\r\n");
+    av_opt_set(rt->stream->priv_data, "headers", headers, 0);
+    av_opt_set(rt->stream->priv_data, "multiple_requests", "1", 0);
+    av_opt_set_bin(rt->stream->priv_data, "post_data", "", 1, 0);
+
+    /* open the http context */
+    if ((ret = ffurl_connect(rt->stream, NULL)) < 0)
+        return ret;
+
+    /* read the server reply which contains a unique ID */
+    for (;;) {
+        ret = ffurl_read(rt->stream, rt->client_id + off, 
sizeof(rt->client_id) - off);
+        if (ret == AVERROR_EOF)
+            break;
+        if (ret < 0)
+            return ret;
+        if (off == sizeof(rt->client_id))
+            return AVERROR(EIO);
+        off += ret;
+    }
+    while (off > 0 && isspace(rt->client_id[off - 1]))
+        rt->client_id[--off] = '\0';
+
+    /* client has now opened the http tunneling connection */
+    rt->state = STATE_OPENED;
+
+    return 0;
+}
+
+URLProtocol ff_rtmphttp_protocol = {
+    .name           = "rtmphttp",
+    .url_open       = rtmp_http_open,
+    .url_read       = rtmp_http_read,
+    .url_write      = rtmp_http_write,
+    .url_close      = rtmp_http_close,
+    .priv_data_size = sizeof(RTMP_HTTPContext),
+    .flags          = URL_PROTOCOL_FLAG_NETWORK,
+};
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index b3ae5a2..9b444f5 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -1112,9 +1112,15 @@ static int rtmp_open(URLContext *s, const char *uri, int 
flags)
     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), 
&port,
                  path, sizeof(path), s->filename);
 
-    if (port < 0)
-        port = RTMP_DEFAULT_PORT;
-    ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
+    if (!strcmp(proto, "rtmpt")) {
+        /* open the http tunneling connection */
+        ff_url_join(buf, sizeof(buf), "rtmphttp", NULL, hostname, port, NULL);
+    } else {
+        /* open the tcp connection */
+        if (port < 0)
+            port = RTMP_DEFAULT_PORT;
+        ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
+    }
 
     if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
                           &s->interrupt_callback, NULL)) < 0) {
@@ -1425,3 +1431,22 @@ URLProtocol ff_rtmp_protocol = {
     .flags          = URL_PROTOCOL_FLAG_NETWORK,
     .priv_data_class= &rtmp_class,
 };
+
+static const AVClass rtmpt_class = {
+    .class_name = "rtmpt",
+    .item_name  = av_default_item_name,
+    .option     = rtmp_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_rtmpt_protocol = {
+    .name            = "rtmpt",
+    .url_open        = rtmp_open,
+    .url_read        = rtmp_read,
+    .url_write       = rtmp_write,
+    .url_close       = rtmp_close,
+    .priv_data_size  = sizeof(RTMPContext),
+    .flags           = URL_PROTOCOL_FLAG_NETWORK,
+    .priv_data_class = &rtmpt_class,
+};
+
diff --git a/libavformat/version.h b/libavformat/version.h
index b00701e..aae0eb1 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -30,8 +30,8 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 54
-#define LIBAVFORMAT_VERSION_MINOR  3
-#define LIBAVFORMAT_VERSION_MICRO  1
+#define LIBAVFORMAT_VERSION_MINOR  4
+#define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
-- 
1.7.10.3

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to