On Wed, 13 Jun 2012, Samuel Pitoiset wrote:

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

The patch is missing proper dependecies in configure. E.g. if you do ./configure --disable-everything --enable-protocol=rtmpt, it should enable everything which is needed (rtmphttp, http, tcp), have a look at how this is handled in configure. Also, building with librtmp doesn't work at the moment, you need to make sure the native rtmpt protocol isn't built if you use librtmp.

diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c
new file mode 100644
index 0000000..17d309f
--- /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 *= 2;

Two flaws: Since out_capacity starts out at 0, you will double the capacity to 0 here. Did you really test this?

Also, even if out_capacity would be something else than zero, say out_capacity == 10, out_size == 8 and size == 40, you'll only double the capacity to 20 but you'll still not be able to fit the full 'size' here. So either of these would be better:

out_capacity = 2*(out_capacity + size);

or

out_capacity = 2*out_capacity + size;

or

out_capacity = 2*(out_size + size);

or

out_capacity = 2*out_size + size;

All of them make sure you have space for at least 'size' more bytes, and adds a bit of headroom to make sure you won't need to reallocate every single time.

+        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;
+    uint8_t tmp_buf[2048];
+    int ret = 0;
+
+    if (rt->state > STATE_OPENED) {
+        // TODO: Add support for the nonblocking variant.
+        if ((ret == rtmp_http_write(h, "", 1)) > 0)

Still a case of ==. When you have such an issue at one place, be sure to doublecheck all of the new code so you don't have more of them.

+            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");
+    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 || off == sizeof(rt->client_id)) {
+            rtmp_http_close(h);
+            return ret;

If off == sizeof(client_id), you return a positive number, making the caller believe this succeeded.

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

Reply via email to