annotate ffmpeg/libavformat/rtmphttp.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents f445c3017523
children
rev   line source
yading@11 1 /*
yading@11 2 * RTMP HTTP network protocol
yading@11 3 * Copyright (c) 2012 Samuel Pitoiset
yading@11 4 *
yading@11 5 * This file is part of FFmpeg.
yading@11 6 *
yading@11 7 * FFmpeg is free software; you can redistribute it and/or
yading@11 8 * modify it under the terms of the GNU Lesser General Public
yading@11 9 * License as published by the Free Software Foundation; either
yading@11 10 * version 2.1 of the License, or (at your option) any later version.
yading@11 11 *
yading@11 12 * FFmpeg is distributed in the hope that it will be useful,
yading@11 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@11 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@11 15 * Lesser General Public License for more details.
yading@11 16 *
yading@11 17 * You should have received a copy of the GNU Lesser General Public
yading@11 18 * License along with FFmpeg; if not, write to the Free Software
yading@11 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@11 20 */
yading@11 21
yading@11 22 /**
yading@11 23 * @file
yading@11 24 * RTMP HTTP protocol
yading@11 25 */
yading@11 26
yading@11 27 #include "libavutil/avstring.h"
yading@11 28 #include "libavutil/intfloat.h"
yading@11 29 #include "libavutil/opt.h"
yading@11 30 #include "libavutil/time.h"
yading@11 31 #include "internal.h"
yading@11 32 #include "http.h"
yading@11 33 #include "rtmp.h"
yading@11 34
yading@11 35 #define RTMPT_DEFAULT_PORT 80
yading@11 36 #define RTMPTS_DEFAULT_PORT RTMPS_DEFAULT_PORT
yading@11 37
yading@11 38 /* protocol handler context */
yading@11 39 typedef struct RTMP_HTTPContext {
yading@11 40 const AVClass *class;
yading@11 41 URLContext *stream; ///< HTTP stream
yading@11 42 char host[256]; ///< hostname of the server
yading@11 43 int port; ///< port to connect (default is 80)
yading@11 44 char client_id[64]; ///< client ID used for all requests except the first one
yading@11 45 int seq; ///< sequence ID used for all requests
yading@11 46 uint8_t *out_data; ///< output buffer
yading@11 47 int out_size; ///< current output buffer size
yading@11 48 int out_capacity; ///< current output buffer capacity
yading@11 49 int initialized; ///< flag indicating when the http context is initialized
yading@11 50 int finishing; ///< flag indicating when the client closes the connection
yading@11 51 int nb_bytes_read; ///< number of bytes read since the last request
yading@11 52 int tls; ///< use Transport Security Layer (RTMPTS)
yading@11 53 } RTMP_HTTPContext;
yading@11 54
yading@11 55 static int rtmp_http_send_cmd(URLContext *h, const char *cmd)
yading@11 56 {
yading@11 57 RTMP_HTTPContext *rt = h->priv_data;
yading@11 58 char uri[2048];
yading@11 59 uint8_t c;
yading@11 60 int ret;
yading@11 61
yading@11 62 ff_url_join(uri, sizeof(uri), "http", NULL, rt->host, rt->port,
yading@11 63 "/%s/%s/%d", cmd, rt->client_id, rt->seq++);
yading@11 64
yading@11 65 av_opt_set_bin(rt->stream->priv_data, "post_data", rt->out_data,
yading@11 66 rt->out_size, 0);
yading@11 67
yading@11 68 /* send a new request to the server */
yading@11 69 if ((ret = ff_http_do_new_request(rt->stream, uri)) < 0)
yading@11 70 return ret;
yading@11 71
yading@11 72 /* re-init output buffer */
yading@11 73 rt->out_size = 0;
yading@11 74
yading@11 75 /* read the first byte which contains the polling interval */
yading@11 76 if ((ret = ffurl_read(rt->stream, &c, 1)) < 0)
yading@11 77 return ret;
yading@11 78
yading@11 79 /* re-init the number of bytes read */
yading@11 80 rt->nb_bytes_read = 0;
yading@11 81
yading@11 82 return ret;
yading@11 83 }
yading@11 84
yading@11 85 static int rtmp_http_write(URLContext *h, const uint8_t *buf, int size)
yading@11 86 {
yading@11 87 RTMP_HTTPContext *rt = h->priv_data;
yading@11 88 void *ptr;
yading@11 89
yading@11 90 if (rt->out_size + size > rt->out_capacity) {
yading@11 91 rt->out_capacity = (rt->out_size + size) * 2;
yading@11 92 ptr = av_realloc(rt->out_data, rt->out_capacity);
yading@11 93 if (!ptr)
yading@11 94 return AVERROR(ENOMEM);
yading@11 95 rt->out_data = ptr;
yading@11 96 }
yading@11 97
yading@11 98 memcpy(rt->out_data + rt->out_size, buf, size);
yading@11 99 rt->out_size += size;
yading@11 100
yading@11 101 return size;
yading@11 102 }
yading@11 103
yading@11 104 static int rtmp_http_read(URLContext *h, uint8_t *buf, int size)
yading@11 105 {
yading@11 106 RTMP_HTTPContext *rt = h->priv_data;
yading@11 107 int ret, off = 0;
yading@11 108
yading@11 109 /* try to read at least 1 byte of data */
yading@11 110 do {
yading@11 111 ret = ffurl_read(rt->stream, buf + off, size);
yading@11 112 if (ret < 0 && ret != AVERROR_EOF)
yading@11 113 return ret;
yading@11 114
yading@11 115 if (ret == AVERROR_EOF) {
yading@11 116 if (rt->finishing) {
yading@11 117 /* Do not send new requests when the client wants to
yading@11 118 * close the connection. */
yading@11 119 return AVERROR(EAGAIN);
yading@11 120 }
yading@11 121
yading@11 122 /* When the client has reached end of file for the last request,
yading@11 123 * we have to send a new request if we have buffered data.
yading@11 124 * Otherwise, we have to send an idle POST. */
yading@11 125 if (rt->out_size > 0) {
yading@11 126 if ((ret = rtmp_http_send_cmd(h, "send")) < 0)
yading@11 127 return ret;
yading@11 128 } else {
yading@11 129 if (rt->nb_bytes_read == 0) {
yading@11 130 /* Wait 50ms before retrying to read a server reply in
yading@11 131 * order to reduce the number of idle requets. */
yading@11 132 av_usleep(50000);
yading@11 133 }
yading@11 134
yading@11 135 if ((ret = rtmp_http_write(h, "", 1)) < 0)
yading@11 136 return ret;
yading@11 137
yading@11 138 if ((ret = rtmp_http_send_cmd(h, "idle")) < 0)
yading@11 139 return ret;
yading@11 140 }
yading@11 141
yading@11 142 if (h->flags & AVIO_FLAG_NONBLOCK) {
yading@11 143 /* no incoming data to handle in nonblocking mode */
yading@11 144 return AVERROR(EAGAIN);
yading@11 145 }
yading@11 146 } else {
yading@11 147 off += ret;
yading@11 148 size -= ret;
yading@11 149 rt->nb_bytes_read += ret;
yading@11 150 }
yading@11 151 } while (off <= 0);
yading@11 152
yading@11 153 return off;
yading@11 154 }
yading@11 155
yading@11 156 static int rtmp_http_close(URLContext *h)
yading@11 157 {
yading@11 158 RTMP_HTTPContext *rt = h->priv_data;
yading@11 159 uint8_t tmp_buf[2048];
yading@11 160 int ret = 0;
yading@11 161
yading@11 162 if (rt->initialized) {
yading@11 163 /* client wants to close the connection */
yading@11 164 rt->finishing = 1;
yading@11 165
yading@11 166 do {
yading@11 167 ret = rtmp_http_read(h, tmp_buf, sizeof(tmp_buf));
yading@11 168 } while (ret > 0);
yading@11 169
yading@11 170 /* re-init output buffer before sending the close command */
yading@11 171 rt->out_size = 0;
yading@11 172
yading@11 173 if ((ret = rtmp_http_write(h, "", 1)) == 1)
yading@11 174 ret = rtmp_http_send_cmd(h, "close");
yading@11 175 }
yading@11 176
yading@11 177 av_freep(&rt->out_data);
yading@11 178 ffurl_close(rt->stream);
yading@11 179
yading@11 180 return ret;
yading@11 181 }
yading@11 182
yading@11 183 static int rtmp_http_open(URLContext *h, const char *uri, int flags)
yading@11 184 {
yading@11 185 RTMP_HTTPContext *rt = h->priv_data;
yading@11 186 char headers[1024], url[1024];
yading@11 187 int ret, off = 0;
yading@11 188
yading@11 189 av_url_split(NULL, 0, NULL, 0, rt->host, sizeof(rt->host), &rt->port,
yading@11 190 NULL, 0, uri);
yading@11 191
yading@11 192 /* This is the first request that is sent to the server in order to
yading@11 193 * register a client on the server and start a new session. The server
yading@11 194 * replies with a unique id (usually a number) that is used by the client
yading@11 195 * for all future requests.
yading@11 196 * Note: the reply doesn't contain a value for the polling interval.
yading@11 197 * A successful connect resets the consecutive index that is used
yading@11 198 * in the URLs. */
yading@11 199 if (rt->tls) {
yading@11 200 if (rt->port < 0)
yading@11 201 rt->port = RTMPTS_DEFAULT_PORT;
yading@11 202 ff_url_join(url, sizeof(url), "https", NULL, rt->host, rt->port, "/open/1");
yading@11 203 } else {
yading@11 204 if (rt->port < 0)
yading@11 205 rt->port = RTMPT_DEFAULT_PORT;
yading@11 206 ff_url_join(url, sizeof(url), "http", NULL, rt->host, rt->port, "/open/1");
yading@11 207 }
yading@11 208
yading@11 209 /* alloc the http context */
yading@11 210 if ((ret = ffurl_alloc(&rt->stream, url, AVIO_FLAG_READ_WRITE, NULL)) < 0)
yading@11 211 goto fail;
yading@11 212
yading@11 213 /* set options */
yading@11 214 snprintf(headers, sizeof(headers),
yading@11 215 "Cache-Control: no-cache\r\n"
yading@11 216 "Content-type: application/x-fcs\r\n"
yading@11 217 "User-Agent: Shockwave Flash\r\n");
yading@11 218 av_opt_set(rt->stream->priv_data, "headers", headers, 0);
yading@11 219 av_opt_set(rt->stream->priv_data, "multiple_requests", "1", 0);
yading@11 220 av_opt_set_bin(rt->stream->priv_data, "post_data", "", 1, 0);
yading@11 221
yading@11 222 /* open the http context */
yading@11 223 if ((ret = ffurl_connect(rt->stream, NULL)) < 0)
yading@11 224 goto fail;
yading@11 225
yading@11 226 /* read the server reply which contains a unique ID */
yading@11 227 for (;;) {
yading@11 228 ret = ffurl_read(rt->stream, rt->client_id + off, sizeof(rt->client_id) - off);
yading@11 229 if (ret == AVERROR_EOF)
yading@11 230 break;
yading@11 231 if (ret < 0)
yading@11 232 goto fail;
yading@11 233 off += ret;
yading@11 234 if (off == sizeof(rt->client_id)) {
yading@11 235 ret = AVERROR(EIO);
yading@11 236 goto fail;
yading@11 237 }
yading@11 238 }
yading@11 239 while (off > 0 && av_isspace(rt->client_id[off - 1]))
yading@11 240 off--;
yading@11 241 rt->client_id[off] = '\0';
yading@11 242
yading@11 243 /* http context is now initialized */
yading@11 244 rt->initialized = 1;
yading@11 245 return 0;
yading@11 246
yading@11 247 fail:
yading@11 248 rtmp_http_close(h);
yading@11 249 return ret;
yading@11 250 }
yading@11 251
yading@11 252 #define OFFSET(x) offsetof(RTMP_HTTPContext, x)
yading@11 253 #define DEC AV_OPT_FLAG_DECODING_PARAM
yading@11 254
yading@11 255 static const AVOption ffrtmphttp_options[] = {
yading@11 256 {"ffrtmphttp_tls", "Use a HTTPS tunneling connection (RTMPTS).", OFFSET(tls), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC},
yading@11 257 { NULL },
yading@11 258 };
yading@11 259
yading@11 260 static const AVClass ffrtmphttp_class = {
yading@11 261 .class_name = "ffrtmphttp",
yading@11 262 .item_name = av_default_item_name,
yading@11 263 .option = ffrtmphttp_options,
yading@11 264 .version = LIBAVUTIL_VERSION_INT,
yading@11 265 };
yading@11 266
yading@11 267 URLProtocol ff_ffrtmphttp_protocol = {
yading@11 268 .name = "ffrtmphttp",
yading@11 269 .url_open = rtmp_http_open,
yading@11 270 .url_read = rtmp_http_read,
yading@11 271 .url_write = rtmp_http_write,
yading@11 272 .url_close = rtmp_http_close,
yading@11 273 .priv_data_size = sizeof(RTMP_HTTPContext),
yading@11 274 .flags = URL_PROTOCOL_FLAG_NETWORK,
yading@11 275 .priv_data_class= &ffrtmphttp_class,
yading@11 276 };