#include "libavutil/avstring.h"
#include "libavutil/intfloat.h"
#include "libavutil/opt.h"
+#include "libavutil/time.h"
#include "internal.h"
#include "http.h"
+#include "rtmp.h"
#define RTMPT_DEFAULT_PORT 80
+#define RTMPTS_DEFAULT_PORT RTMPS_DEFAULT_PORT
/* protocol handler context */
typedef struct RTMP_HTTPContext {
+ const AVClass *class;
URLContext *stream; ///< HTTP stream
char host[256]; ///< hostname of the server
int port; ///< port to connect (default is 80)
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
+ int nb_bytes_read; ///< number of bytes read since the last request
+ int tls; ///< use Transport Security Layer (RTMPTS)
} RTMP_HTTPContext;
static int rtmp_http_send_cmd(URLContext *h, const char *cmd)
if ((ret = ffurl_read(rt->stream, &c, 1)) < 0)
return ret;
+ /* re-init the number of bytes read */
+ rt->nb_bytes_read = 0;
+
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) {
+ int err;
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;
+ if ((err = av_reallocp(&rt->out_data, rt->out_capacity)) < 0) {
+ rt->out_size = 0;
+ rt->out_capacity = 0;
+ return err;
+ }
}
memcpy(rt->out_data + rt->out_size, buf, size);
if (ret < 0 && ret != AVERROR_EOF)
return ret;
- if (ret == AVERROR_EOF) {
+ if (!ret || ret == AVERROR_EOF) {
if (rt->finishing) {
/* Do not send new requests when the client wants to
* close the connection. */
if ((ret = rtmp_http_send_cmd(h, "send")) < 0)
return ret;
} else {
+ if (rt->nb_bytes_read == 0) {
+ /* Wait 50ms before retrying to read a server reply in
+ * order to reduce the number of idle requets. */
+ av_usleep(50000);
+ }
+
if ((ret = rtmp_http_write(h, "", 1)) < 0)
return ret;
} else {
off += ret;
size -= ret;
+ rt->nb_bytes_read += ret;
}
} while (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
* 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");
+ if (rt->tls) {
+ if (rt->port < 0)
+ rt->port = RTMPTS_DEFAULT_PORT;
+ ff_url_join(url, sizeof(url), "https", NULL, rt->host, rt->port, "/open/1");
+ } else {
+ if (rt->port < 0)
+ rt->port = RTMPT_DEFAULT_PORT;
+ 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)
/* 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)
+ if (!ret || ret == AVERROR_EOF)
break;
if (ret < 0)
goto fail;
goto fail;
}
}
- while (off > 0 && isspace(rt->client_id[off - 1]))
+ while (off > 0 && av_isspace(rt->client_id[off - 1]))
off--;
rt->client_id[off] = '\0';
return ret;
}
-URLProtocol ff_rtmphttp_protocol = {
- .name = "rtmphttp",
+#define OFFSET(x) offsetof(RTMP_HTTPContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption ffrtmphttp_options[] = {
+ {"ffrtmphttp_tls", "Use a HTTPS tunneling connection (RTMPTS).", OFFSET(tls), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC},
+ { NULL },
+};
+
+static const AVClass ffrtmphttp_class = {
+ .class_name = "ffrtmphttp",
+ .item_name = av_default_item_name,
+ .option = ffrtmphttp_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+URLProtocol ff_ffrtmphttp_protocol = {
+ .name = "ffrtmphttp",
.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,
+ .priv_data_class= &ffrtmphttp_class,
};