lavr: add x86-optimized functions for mixing 1-to-2 s16p with flt coeffs
[ffmpeg.git] / libavformat / rtmphttp.c
1 /*
2  * RTMP HTTP network protocol
3  * Copyright (c) 2012 Samuel Pitoiset
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  * RTMP HTTP protocol
25  */
26
27 #include "libavutil/avstring.h"
28 #include "libavutil/intfloat.h"
29 #include "libavutil/opt.h"
30 #include "internal.h"
31 #include "http.h"
32
33 #define RTMPT_DEFAULT_PORT 80
34
35 /* protocol handler context */
36 typedef struct RTMP_HTTPContext {
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 } RTMP_HTTPContext;
48
49 static int rtmp_http_send_cmd(URLContext *h, const char *cmd)
50 {
51     RTMP_HTTPContext *rt = h->priv_data;
52     char uri[2048];
53     uint8_t c;
54     int ret;
55
56     ff_url_join(uri, sizeof(uri), "http", NULL, rt->host, rt->port,
57                 "/%s/%s/%d", cmd, rt->client_id, rt->seq++);
58
59     av_opt_set_bin(rt->stream->priv_data, "post_data", rt->out_data,
60                    rt->out_size, 0);
61
62     /* send a new request to the server */
63     if ((ret = ff_http_do_new_request(rt->stream, uri)) < 0)
64         return ret;
65
66     /* re-init output buffer */
67     rt->out_size = 0;
68
69     /* read the first byte which contains the polling interval */
70     if ((ret = ffurl_read(rt->stream, &c, 1)) < 0)
71         return ret;
72
73     return ret;
74 }
75
76 static int rtmp_http_write(URLContext *h, const uint8_t *buf, int size)
77 {
78     RTMP_HTTPContext *rt = h->priv_data;
79     void *ptr;
80
81     if (rt->out_size + size > rt->out_capacity) {
82         rt->out_capacity = (rt->out_size + size) * 2;
83         ptr = av_realloc(rt->out_data, rt->out_capacity);
84         if (!ptr)
85             return AVERROR(ENOMEM);
86         rt->out_data = ptr;
87     }
88
89     memcpy(rt->out_data + rt->out_size, buf, size);
90     rt->out_size += size;
91
92     return size;
93 }
94
95 static int rtmp_http_read(URLContext *h, uint8_t *buf, int size)
96 {
97     RTMP_HTTPContext *rt = h->priv_data;
98     int ret, off = 0;
99
100     /* try to read at least 1 byte of data */
101     do {
102         ret = ffurl_read(rt->stream, buf + off, size);
103         if (ret < 0 && ret != AVERROR_EOF)
104             return ret;
105
106         if (ret == AVERROR_EOF) {
107             if (rt->finishing) {
108                 /* Do not send new requests when the client wants to
109                  * close the connection. */
110                 return AVERROR(EAGAIN);
111             }
112
113             /* When the client has reached end of file for the last request,
114              * we have to send a new request if we have buffered data.
115              * Otherwise, we have to send an idle POST. */
116             if (rt->out_size > 0) {
117                 if ((ret = rtmp_http_send_cmd(h, "send")) < 0)
118                     return ret;
119             } else {
120                 if ((ret = rtmp_http_write(h, "", 1)) < 0)
121                     return ret;
122
123                 if ((ret = rtmp_http_send_cmd(h, "idle")) < 0)
124                     return ret;
125             }
126
127             if (h->flags & AVIO_FLAG_NONBLOCK) {
128                 /* no incoming data to handle in nonblocking mode */
129                 return AVERROR(EAGAIN);
130             }
131         } else {
132             off  += ret;
133             size -= ret;
134         }
135     } while (off <= 0);
136
137     return off;
138 }
139
140 static int rtmp_http_close(URLContext *h)
141 {
142     RTMP_HTTPContext *rt = h->priv_data;
143     uint8_t tmp_buf[2048];
144     int ret = 0;
145
146     if (rt->initialized) {
147         /* client wants to close the connection */
148         rt->finishing = 1;
149
150         do {
151             ret = rtmp_http_read(h, tmp_buf, sizeof(tmp_buf));
152         } while (ret > 0);
153
154         /* re-init output buffer before sending the close command */
155         rt->out_size = 0;
156
157         if ((ret = rtmp_http_write(h, "", 1)) == 1)
158             ret = rtmp_http_send_cmd(h, "close");
159     }
160
161     av_freep(&rt->out_data);
162     ffurl_close(rt->stream);
163
164     return ret;
165 }
166
167 static int rtmp_http_open(URLContext *h, const char *uri, int flags)
168 {
169     RTMP_HTTPContext *rt = h->priv_data;
170     char headers[1024], url[1024];
171     int ret, off = 0;
172
173     av_url_split(NULL, 0, NULL, 0, rt->host, sizeof(rt->host), &rt->port,
174                  NULL, 0, uri);
175
176     if (rt->port < 0)
177         rt->port = RTMPT_DEFAULT_PORT;
178
179     /* This is the first request that is sent to the server in order to
180      * register a client on the server and start a new session. The server
181      * replies with a unique id (usually a number) that is used by the client
182      * for all future requests.
183      * Note: the reply doesn't contain a value for the polling interval.
184      * A successful connect resets the consecutive index that is used
185      * in the URLs. */
186     ff_url_join(url, sizeof(url), "http", NULL, rt->host, rt->port, "/open/1");
187
188     /* alloc the http context */
189     if ((ret = ffurl_alloc(&rt->stream, url, AVIO_FLAG_READ_WRITE, NULL)) < 0)
190         goto fail;
191
192     /* set options */
193     snprintf(headers, sizeof(headers),
194              "Cache-Control: no-cache\r\n"
195              "Content-type: application/x-fcs\r\n"
196              "User-Agent: Shockwave Flash\r\n");
197     av_opt_set(rt->stream->priv_data, "headers", headers, 0);
198     av_opt_set(rt->stream->priv_data, "multiple_requests", "1", 0);
199     av_opt_set_bin(rt->stream->priv_data, "post_data", "", 1, 0);
200
201     /* open the http context */
202     if ((ret = ffurl_connect(rt->stream, NULL)) < 0)
203         goto fail;
204
205     /* read the server reply which contains a unique ID */
206     for (;;) {
207         ret = ffurl_read(rt->stream, rt->client_id + off, sizeof(rt->client_id) - off);
208         if (ret == AVERROR_EOF)
209             break;
210         if (ret < 0)
211             goto fail;
212         off += ret;
213         if (off == sizeof(rt->client_id)) {
214             ret = AVERROR(EIO);
215             goto fail;
216         }
217     }
218     while (off > 0 && isspace(rt->client_id[off - 1]))
219         off--;
220     rt->client_id[off] = '\0';
221
222     /* http context is now initialized */
223     rt->initialized = 1;
224     return 0;
225
226 fail:
227     rtmp_http_close(h);
228     return ret;
229 }
230
231 URLProtocol ff_rtmphttp_protocol = {
232     .name           = "rtmphttp",
233     .url_open       = rtmp_http_open,
234     .url_read       = rtmp_http_read,
235     .url_write      = rtmp_http_write,
236     .url_close      = rtmp_http_close,
237     .priv_data_size = sizeof(RTMP_HTTPContext),
238     .flags          = URL_PROTOCOL_FLAG_NETWORK,
239 };