Commit 8e50c57d authored by Samuel Pitoiset's avatar Samuel Pitoiset Committed by Martin Storsjö

RTMPT protocol support

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.
Signed-off-by: 's avatarMartin Storsjö <martin@martin.st>
parent 35127bf1
......@@ -25,6 +25,7 @@ version <next>:
be used with -of old.
- Indeo Audio decoder
- channelsplit audio filter
- RTMPT protocol support
version 0.8:
......
......@@ -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"
......
......@@ -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
......
......@@ -345,6 +345,8 @@ 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_RTMPT_PROTOCOL) += rtmpproto.o rtmppkt.o
OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o
OBJS-$(CONFIG_SCTP_PROTOCOL) += sctp.o
OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o
......
......@@ -256,6 +256,8 @@ void av_register_all(void)
REGISTER_PROTOCOL (MD5, md5);
REGISTER_PROTOCOL (PIPE, pipe);
REGISTER_PROTOCOL (RTMP, rtmp);
REGISTER_PROTOCOL (RTMPHTTP, rtmphttp);
REGISTER_PROTOCOL (RTMPT, rtmpt);
REGISTER_PROTOCOL (RTP, rtp);
REGISTER_PROTOCOL (SCTP, sctp);
REGISTER_PROTOCOL (TCP, tcp);
......
/*
* 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
int initialized; ///< flag indicating when the http context is initialized
int finishing; ///< flag indicating when the client closes the connection
} 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) {
if (rt->finishing) {
/* Do not send new requests when the client wants to
* close the connection. */
return AVERROR(EAGAIN);
}
/* 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;
}
if (h->flags & AVIO_FLAG_NONBLOCK) {
/* no incoming data to handle in nonblocking mode */
return AVERROR(EAGAIN);
}
} 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->initialized) {
/* client wants to close the connection */
rt->finishing = 1;
do {
ret = rtmp_http_read(h, tmp_buf, sizeof(tmp_buf));
} while (ret > 0);
/* re-init output buffer before sending the close command */
rt->out_size = 0;
if ((ret = rtmp_http_write(h, "", 1)) == 1)
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)
goto fail;
/* 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)
goto fail;
/* 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)
goto fail;
off += ret;
if (off == sizeof(rt->client_id)) {
ret = AVERROR(EIO);
goto fail;
}
}
while (off > 0 && isspace(rt->client_id[off - 1]))
off--;
rt->client_id[off] = '\0';
/* http context is now initialized */
rt->initialized = 1;
return 0;
fail:
rtmp_http_close(h);
return ret;
}
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,
};
......@@ -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,21 @@ 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,
};
......@@ -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, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment