00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #include "libavutil/avstring.h"
00028 #include "libavutil/intfloat.h"
00029 #include "libavutil/opt.h"
00030 #include "libavutil/time.h"
00031 #include "internal.h"
00032 #include "http.h"
00033 #include "rtmp.h"
00034
00035 #define RTMPT_DEFAULT_PORT 80
00036 #define RTMPTS_DEFAULT_PORT RTMPS_DEFAULT_PORT
00037
00038
00039 typedef struct RTMP_HTTPContext {
00040 const AVClass *class;
00041 URLContext *stream;
00042 char host[256];
00043 int port;
00044 char client_id[64];
00045 int seq;
00046 uint8_t *out_data;
00047 int out_size;
00048 int out_capacity;
00049 int initialized;
00050 int finishing;
00051 int nb_bytes_read;
00052 int tls;
00053 } RTMP_HTTPContext;
00054
00055 static int rtmp_http_send_cmd(URLContext *h, const char *cmd)
00056 {
00057 RTMP_HTTPContext *rt = h->priv_data;
00058 char uri[2048];
00059 uint8_t c;
00060 int ret;
00061
00062 ff_url_join(uri, sizeof(uri), "http", NULL, rt->host, rt->port,
00063 "/%s/%s/%d", cmd, rt->client_id, rt->seq++);
00064
00065 av_opt_set_bin(rt->stream->priv_data, "post_data", rt->out_data,
00066 rt->out_size, 0);
00067
00068
00069 if ((ret = ff_http_do_new_request(rt->stream, uri)) < 0)
00070 return ret;
00071
00072
00073 rt->out_size = 0;
00074
00075
00076 if ((ret = ffurl_read(rt->stream, &c, 1)) < 0)
00077 return ret;
00078
00079
00080 rt->nb_bytes_read = 0;
00081
00082 return ret;
00083 }
00084
00085 static int rtmp_http_write(URLContext *h, const uint8_t *buf, int size)
00086 {
00087 RTMP_HTTPContext *rt = h->priv_data;
00088 void *ptr;
00089
00090 if (rt->out_size + size > rt->out_capacity) {
00091 rt->out_capacity = (rt->out_size + size) * 2;
00092 ptr = av_realloc(rt->out_data, rt->out_capacity);
00093 if (!ptr)
00094 return AVERROR(ENOMEM);
00095 rt->out_data = ptr;
00096 }
00097
00098 memcpy(rt->out_data + rt->out_size, buf, size);
00099 rt->out_size += size;
00100
00101 return size;
00102 }
00103
00104 static int rtmp_http_read(URLContext *h, uint8_t *buf, int size)
00105 {
00106 RTMP_HTTPContext *rt = h->priv_data;
00107 int ret, off = 0;
00108
00109
00110 do {
00111 ret = ffurl_read(rt->stream, buf + off, size);
00112 if (ret < 0 && ret != AVERROR_EOF)
00113 return ret;
00114
00115 if (ret == AVERROR_EOF) {
00116 if (rt->finishing) {
00117
00118
00119 return AVERROR(EAGAIN);
00120 }
00121
00122
00123
00124
00125 if (rt->out_size > 0) {
00126 if ((ret = rtmp_http_send_cmd(h, "send")) < 0)
00127 return ret;
00128 } else {
00129 if (rt->nb_bytes_read == 0) {
00130
00131
00132 av_usleep(50000);
00133 }
00134
00135 if ((ret = rtmp_http_write(h, "", 1)) < 0)
00136 return ret;
00137
00138 if ((ret = rtmp_http_send_cmd(h, "idle")) < 0)
00139 return ret;
00140 }
00141
00142 if (h->flags & AVIO_FLAG_NONBLOCK) {
00143
00144 return AVERROR(EAGAIN);
00145 }
00146 } else {
00147 off += ret;
00148 size -= ret;
00149 rt->nb_bytes_read += ret;
00150 }
00151 } while (off <= 0);
00152
00153 return off;
00154 }
00155
00156 static int rtmp_http_close(URLContext *h)
00157 {
00158 RTMP_HTTPContext *rt = h->priv_data;
00159 uint8_t tmp_buf[2048];
00160 int ret = 0;
00161
00162 if (rt->initialized) {
00163
00164 rt->finishing = 1;
00165
00166 do {
00167 ret = rtmp_http_read(h, tmp_buf, sizeof(tmp_buf));
00168 } while (ret > 0);
00169
00170
00171 rt->out_size = 0;
00172
00173 if ((ret = rtmp_http_write(h, "", 1)) == 1)
00174 ret = rtmp_http_send_cmd(h, "close");
00175 }
00176
00177 av_freep(&rt->out_data);
00178 ffurl_close(rt->stream);
00179
00180 return ret;
00181 }
00182
00183 static int rtmp_http_open(URLContext *h, const char *uri, int flags)
00184 {
00185 RTMP_HTTPContext *rt = h->priv_data;
00186 char headers[1024], url[1024];
00187 int ret, off = 0;
00188
00189 av_url_split(NULL, 0, NULL, 0, rt->host, sizeof(rt->host), &rt->port,
00190 NULL, 0, uri);
00191
00192
00193
00194
00195
00196
00197
00198
00199 if (rt->tls) {
00200 if (rt->port < 0)
00201 rt->port = RTMPTS_DEFAULT_PORT;
00202 ff_url_join(url, sizeof(url), "https", NULL, rt->host, rt->port, "/open/1");
00203 } else {
00204 if (rt->port < 0)
00205 rt->port = RTMPT_DEFAULT_PORT;
00206 ff_url_join(url, sizeof(url), "http", NULL, rt->host, rt->port, "/open/1");
00207 }
00208
00209
00210 if ((ret = ffurl_alloc(&rt->stream, url, AVIO_FLAG_READ_WRITE, NULL)) < 0)
00211 goto fail;
00212
00213
00214 snprintf(headers, sizeof(headers),
00215 "Cache-Control: no-cache\r\n"
00216 "Content-type: application/x-fcs\r\n"
00217 "User-Agent: Shockwave Flash\r\n");
00218 av_opt_set(rt->stream->priv_data, "headers", headers, 0);
00219 av_opt_set(rt->stream->priv_data, "multiple_requests", "1", 0);
00220 av_opt_set_bin(rt->stream->priv_data, "post_data", "", 1, 0);
00221
00222
00223 if ((ret = ffurl_connect(rt->stream, NULL)) < 0)
00224 goto fail;
00225
00226
00227 for (;;) {
00228 ret = ffurl_read(rt->stream, rt->client_id + off, sizeof(rt->client_id) - off);
00229 if (ret == AVERROR_EOF)
00230 break;
00231 if (ret < 0)
00232 goto fail;
00233 off += ret;
00234 if (off == sizeof(rt->client_id)) {
00235 ret = AVERROR(EIO);
00236 goto fail;
00237 }
00238 }
00239 while (off > 0 && isspace(rt->client_id[off - 1]))
00240 off--;
00241 rt->client_id[off] = '\0';
00242
00243
00244 rt->initialized = 1;
00245 return 0;
00246
00247 fail:
00248 rtmp_http_close(h);
00249 return ret;
00250 }
00251
00252 #define OFFSET(x) offsetof(RTMP_HTTPContext, x)
00253 #define DEC AV_OPT_FLAG_DECODING_PARAM
00254
00255 static const AVOption ffrtmphttp_options[] = {
00256 {"ffrtmphttp_tls", "Use a HTTPS tunneling connection (RTMPTS).", OFFSET(tls), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC},
00257 { NULL },
00258 };
00259
00260 static const AVClass ffrtmphttp_class = {
00261 .class_name = "ffrtmphttp",
00262 .item_name = av_default_item_name,
00263 .option = ffrtmphttp_options,
00264 .version = LIBAVUTIL_VERSION_INT,
00265 };
00266
00267 URLProtocol ff_ffrtmphttp_protocol = {
00268 .name = "ffrtmphttp",
00269 .url_open = rtmp_http_open,
00270 .url_read = rtmp_http_read,
00271 .url_write = rtmp_http_write,
00272 .url_close = rtmp_http_close,
00273 .priv_data_size = sizeof(RTMP_HTTPContext),
00274 .flags = URL_PROTOCOL_FLAG_NETWORK,
00275 .priv_data_class= &ffrtmphttp_class,
00276 };