On Tue, Jun 12, 2012 at 9:35 PM, Martin Storsjö <mar...@martin.st> wrote:
> On Mon, 11 Jun 2012, Samuel Pitoiset wrote:
>
>> diff --git a/Changelog b/Changelog
>> index 898a247..d901923 100644
>> --- a/Changelog
>> +++ b/Changelog
>> @@ -24,6 +24,7 @@ version <next>:
>> - avprobe output is now standard INI or JSON. The old format can still
>>  be used with -of old.
>> - Indeo Audio decoder
>> +- RTMPT support
>>
>>
>> version 0.8:
>> diff --git a/doc/general.texi b/doc/general.texi
>> index dee8f9e..29ef864 100644
>> --- a/doc/general.texi
>> +++ b/doc/general.texi
>> @@ -810,6 +810,7 @@ performance on systems without hardware floating point
>> support).
>> @item HTTP         @tab X
>> @item MMS          @tab X
>> @item pipe         @tab X
>> +@item RTMPT        @tab x
>> @item RTP          @tab X
>
>
> Since the normal RTMP isn't mentioned here, this looks a bit strange -
> please add it first (in a separate patch).

Done in a separate patch (not submitted yet).

>
>> @item TCP          @tab X
>> @item UDP          @tab X
>> diff --git a/doc/protocols.texi b/doc/protocols.texi
>> index 172184e..eabb26d 100644
>> --- a/doc/protocols.texi
>> +++ b/doc/protocols.texi
>> @@ -193,6 +193,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 in HTTP.
>> +
>> +The Real-Time Messaging Protocol tunneled in 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..5f1cafb 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
>> 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..1e474a8
>> --- /dev/null
>> +++ b/libavformat/rtmphttp.c
>> @@ -0,0 +1,210 @@
>> +/*
>> + * 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 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
>> +    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 += size;
>
>
> In code like this, one might want to increase the capacity with a bit more
> than the size (like += size*3/2, or by doubling capacity), to reduce the
> number of realloc calls.

Okay, I'll double the capacity here.

>
>> +        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;
>
>
> Note that you've used == instead of = here, this won't do the right thing.
> (Some compiler perhaps might even warn that the comparison won't ever be
> true.) The same mistake seems to be repeated a few times - make sure you
> find and fix all of them.

mmh... I know that is totally wrong. Typo errors I guess.
>
>> +
>> +                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 ((ret == rtmp_http_write(h, "", 1)) > 0) {
>> +        for (;;) {
>> +            /* consume data from the current request */
>> +            ret = rtmp_http_read(h, tmp_buf, sizeof(tmp_buf));
>> +            if (ret < 0)
>> +                break;
>> +        }
>
>
> Does this work correctly? Won't rtmp_http_read just keep sending new idle
> posts, to which the server will respond with empty replies, looping
> infinitely here? To do this properly I think you'll have to reintroduce the
> state variable saying whether you still have an ongoing request or not, and
> you might need to add support for the nonblocking variant of reading.
> Otherwise, rtmp_http_read will only return once there's at least 1 byte to
> return, and the server won't break the connection.

Ok.

>
>> +        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);
>> +
>> +    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;
>> +        }
>> +        off += ret;
>> +    }
>> +    rt->client_id[off - 1] = '\0';
>
>
> This will write out of bounds if off == 0. Also, I've twice requested you to
> do this newline trimming more generally, by trimming all whitespace chars at
> the end of the string. Please either do that or respond saying why you
> didn't do it.

This code removes the newline... so I'm a little confused...

>
> The patch also still will break if publishing data, since the general rtmp
> proto lacks readyness for that, and you need to implement the "nonblocking"
> read mode in this proto, which I described earlier.

Indeed, I still have to fix rtmp_write.



-- 
Best regards,
Samuel Pitoiset.
_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to