rtmphttp.c 8.7 KB

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